31
README.md
31
README.md
@@ -176,6 +176,9 @@ Edit `.env` file:
|
|||||||
PORT=3000
|
PORT=3000
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
|
# API Domain/IP Configuration (for Swagger docs and CORS)
|
||||||
|
API_DOMAIN=localhost:3000
|
||||||
|
|
||||||
# Security Configuration
|
# Security Configuration
|
||||||
ALLOW_REDEEM_DOMAINS=ln.tips,getalby.com,wallet.mutinywallet.com
|
ALLOW_REDEEM_DOMAINS=ln.tips,getalby.com,wallet.mutinywallet.com
|
||||||
API_SECRET=your-secret-key-here
|
API_SECRET=your-secret-key-here
|
||||||
@@ -199,6 +202,34 @@ npm run dev
|
|||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
#### API_DOMAIN Configuration
|
||||||
|
The `API_DOMAIN` environment variable is used to configure the correct domain/IP for your API in production. This affects:
|
||||||
|
|
||||||
|
- **Swagger Documentation**: The "Try it out" feature will use the correct server URL
|
||||||
|
- **CORS Configuration**: Default CORS origins will use the correct protocol and domain
|
||||||
|
- **API Documentation**: Server URLs in the documentation will be accurate
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
# Development
|
||||||
|
API_DOMAIN=localhost:3000
|
||||||
|
|
||||||
|
# Production with domain
|
||||||
|
API_DOMAIN=api.yourdomain.com
|
||||||
|
|
||||||
|
# Production with IP
|
||||||
|
API_DOMAIN=192.168.1.100:3000
|
||||||
|
|
||||||
|
# Production with custom port
|
||||||
|
API_DOMAIN=yourdomain.com:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The protocol (http/https) is automatically determined based on `NODE_ENV`:
|
||||||
|
- `NODE_ENV=development` → `http://`
|
||||||
|
- `NODE_ENV=production` → `https://`
|
||||||
|
|
||||||
The API will be available at `http://localhost:3000`
|
The API will be available at `http://localhost:3000`
|
||||||
|
|
||||||
## 🔧 Configuration
|
## 🔧 Configuration
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
PORT=3000
|
PORT=3000
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
|
# API Domain/IP Configuration (for Swagger docs and CORS)
|
||||||
|
API_DOMAIN=localhost:3000
|
||||||
|
|
||||||
# Security Configuration
|
# Security Configuration
|
||||||
ALLOW_REDEEM_DOMAINS=*
|
ALLOW_REDEEM_DOMAINS=*
|
||||||
|
|
||||||
|
|
||||||
# Default Lightning Address (used when no address is provided in redeem requests)
|
# Default Lightning Address (used when no address is provided in redeem requests)
|
||||||
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
|
DEFAULT_LIGHTNING_ADDRESS=admin@your-domain.com
|
||||||
|
|
||||||
|
|||||||
87
server.js
87
server.js
@@ -10,23 +10,56 @@ const redemptionService = require('./services/redemption');
|
|||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
// Get API domain for CORS configuration
|
||||||
|
const apiDomain = process.env.API_DOMAIN || 'localhost:3000';
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const protocol = isProduction ? 'https' : 'http';
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(express.json({ limit: '10mb' }));
|
app.use(express.json({ limit: '10mb' }));
|
||||||
|
|
||||||
|
// Enhanced CORS configuration for Swagger UI
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: process.env.ALLOWED_ORIGINS
|
origin: process.env.ALLOWED_ORIGINS
|
||||||
? process.env.ALLOWED_ORIGINS.split(',').map(o => o.trim())
|
? process.env.ALLOWED_ORIGINS.split(',').map(o => o.trim())
|
||||||
: ['http://localhost:3000'],
|
: [`${protocol}://${apiDomain}`],
|
||||||
methods: ['GET', 'POST'],
|
methods: ['GET', 'POST', 'OPTIONS'],
|
||||||
allowedHeaders: ['Content-Type', 'Authorization']
|
allowedHeaders: ['Content-Type', 'Authorization', 'Accept', 'Origin', 'X-Requested-With'],
|
||||||
|
credentials: true,
|
||||||
|
optionsSuccessStatus: 200
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Additional middleware for Swagger UI preflight requests
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
|
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||||
|
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Accept, Origin, X-Requested-With');
|
||||||
|
res.status(200).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Debug endpoint to test CORS
|
||||||
|
app.get('/api/cors-test', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'CORS test successful',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
origin: req.headers.origin,
|
||||||
|
host: req.headers.host
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Swagger Documentation
|
// Swagger Documentation
|
||||||
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs, {
|
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs, {
|
||||||
customCss: '.swagger-ui .topbar { display: none }',
|
customCss: '.swagger-ui .topbar { display: none }',
|
||||||
customSiteTitle: 'Cashu Redeem API Documentation',
|
customSiteTitle: 'Cashu Redeem API Documentation',
|
||||||
swaggerOptions: {
|
swaggerOptions: {
|
||||||
filter: true,
|
filter: true,
|
||||||
showRequestHeaders: true
|
showRequestHeaders: true,
|
||||||
|
tryItOutEnabled: true
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -79,52 +112,6 @@ function asyncHandler(fn) {
|
|||||||
|
|
||||||
// API Routes
|
// API Routes
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /:
|
|
||||||
* get:
|
|
||||||
* summary: API Information
|
|
||||||
* description: Get basic information about the Cashu Redeem API
|
|
||||||
* tags: [General]
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: API information
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* name:
|
|
||||||
* type: string
|
|
||||||
* example: "Cashu Redeem API"
|
|
||||||
* version:
|
|
||||||
* type: string
|
|
||||||
* example: "1.0.0"
|
|
||||||
* description:
|
|
||||||
* type: string
|
|
||||||
* example: "A production-grade API for redeeming Cashu tokens (ecash) to Lightning addresses"
|
|
||||||
* documentation:
|
|
||||||
* type: string
|
|
||||||
* example: "/docs"
|
|
||||||
* endpoints:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* decode:
|
|
||||||
* type: string
|
|
||||||
* example: "POST /api/decode"
|
|
||||||
* redeem:
|
|
||||||
* type: string
|
|
||||||
* example: "POST /api/redeem"
|
|
||||||
* validate:
|
|
||||||
* type: string
|
|
||||||
* example: "POST /api/validate-address"
|
|
||||||
* health:
|
|
||||||
* type: string
|
|
||||||
* example: "GET /api/health"
|
|
||||||
* github:
|
|
||||||
* type: string
|
|
||||||
* example: "https://github.com/yourusername/cashu-redeem-api"
|
|
||||||
*/
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
name: 'Cashu Redeem API',
|
name: 'Cashu Redeem API',
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
|
require('dotenv').config();
|
||||||
const swaggerJsdoc = require('swagger-jsdoc');
|
const swaggerJsdoc = require('swagger-jsdoc');
|
||||||
|
|
||||||
|
// Get the API domain from environment variable, default to localhost:3000
|
||||||
|
const apiDomain = process.env.API_DOMAIN || 'localhost:3000';
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const protocol = isProduction ? 'https' : 'http';
|
||||||
|
|
||||||
|
// For production behind Nginx, we need to ensure the URL doesn't include the internal port
|
||||||
|
const serverUrl = isProduction
|
||||||
|
? `${protocol}://${apiDomain}`
|
||||||
|
: `${protocol}://${apiDomain}`;
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
definition: {
|
definition: {
|
||||||
openapi: '3.0.0',
|
openapi: '3.0.0',
|
||||||
@@ -18,12 +29,8 @@ const options = {
|
|||||||
},
|
},
|
||||||
servers: [
|
servers: [
|
||||||
{
|
{
|
||||||
url: 'http://localhost:3000',
|
url: serverUrl,
|
||||||
description: 'Development server'
|
description: isProduction ? 'Production server' : 'Development server'
|
||||||
},
|
|
||||||
{
|
|
||||||
url: 'https://api.example.com',
|
|
||||||
description: 'Production server'
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
@@ -323,10 +330,6 @@ const options = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
tags: [
|
tags: [
|
||||||
{
|
|
||||||
name: 'General',
|
|
||||||
description: 'General API information and utilities'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'Token Operations',
|
name: 'Token Operations',
|
||||||
description: 'Operations for decoding and redeeming Cashu tokens'
|
description: 'Operations for decoding and redeeming Cashu tokens'
|
||||||
|
|||||||
Reference in New Issue
Block a user