mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-18 06:41:27 +02:00
Add health check endpoint and missing REST API endpoints.
This commit is contained in:
@@ -44,15 +44,44 @@ If the wallet RPC service or configuration is unavailable the backend automatica
|
||||
|
||||
## API Surface
|
||||
|
||||
- `POST /api/payment/create` – Request a payment for the active mode.
|
||||
- `GET /api/payment/status/:paymentId` – Poll payment status.
|
||||
- `POST /api/payment/callback` – Wallet RPC webhook to confirm payments.
|
||||
- `GET /api/game/modes` – Current game modes and pricing.
|
||||
- `GET /api/user/credits/:userId` – Remaining credit balance.
|
||||
- `GET /api/user/mode/:userId` – User game mode.
|
||||
- `POST /api/user/address/:userId` – Store payout address after confirmation.
|
||||
### Health & Status
|
||||
|
||||
Only the payment routes require wallet RPC access; all other gameplay functions work without it.
|
||||
- `GET /health` – Server health check with uptime, memory usage, game counts, and wallet status.
|
||||
- `GET /api/game-modes` – Current game modes and pricing configuration.
|
||||
|
||||
### User Endpoints
|
||||
|
||||
- `GET /api/user/:socketId/credits` – Remaining credit balance for a user.
|
||||
- `GET /api/user/:socketId/mode` – User payment mode preferences and enabled features.
|
||||
- `POST /api/user/:socketId/address` – Store payout address (body: `{ "address": "<XMR/WOW address>" }`).
|
||||
|
||||
### Payment Endpoints (Socket.IO Preferred)
|
||||
|
||||
The following REST endpoints exist but return `501 Not Implemented`. Payment flows are handled via Socket.IO events for real-time feedback:
|
||||
|
||||
- `POST /api/payment/create` – Use socket event `request_payment` instead.
|
||||
- `GET /api/payment/status/:paymentId` – Use socket event `payment_status` instead.
|
||||
- `POST /api/payment/callback` – Reserved for future webhook integration.
|
||||
|
||||
### Socket.IO Events (Primary Payment Flow)
|
||||
|
||||
| Client → Server | Description |
|
||||
|-----------------|-------------|
|
||||
| `request_payment` | Request a payment address for game entry |
|
||||
| `confirm_address` | Confirm a detected payout address |
|
||||
| `cancel_address` | Cancel pending address confirmation |
|
||||
| `join_queue` | Join the game queue after payment |
|
||||
|
||||
| Server → Client | Description |
|
||||
|-----------------|-------------|
|
||||
| `payment_created` | Payment address and QR code ready |
|
||||
| `payment_confirmed` | Payment received and confirmed |
|
||||
| `address_detected` | Payout address detected in chat |
|
||||
| `address_confirmed` | Payout address saved |
|
||||
| `game_start` | Game session beginning |
|
||||
| `credits_update` | Credit balance changed |
|
||||
|
||||
All gameplay functions work without wallet RPC access; only paid modes require it.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Wownerogue Development Todo
|
||||
|
||||
## High Priority
|
||||
- [ ] **Verify Server Startup**: Ensure `npm run dev` starts cleanly without errors after recent refactors.
|
||||
- [x] **Verify Server Startup**: Server starts cleanly with `npm run dev` ✅
|
||||
- [ ] **Test Payment Flow**: Verify the full payment lifecycle (Request -> QR -> Payment -> Confirmation -> Game Start).
|
||||
- [ ] **Test Payouts**: Confirm payouts are triggered correctly upon winning in paid modes.
|
||||
- [ ] **Address Persistence**: Verify that payout addresses persist across server restarts via session tokens.
|
||||
@@ -13,5 +13,15 @@
|
||||
- [ ] **Frontend Cleanup**: Remove any remaining dead code or unused assets from the `html/` directory.
|
||||
|
||||
## Documentation
|
||||
- [ ] **API Documentation**: Expand API documentation in `README.md` with request/response examples.
|
||||
- [x] **API Documentation**: README updated with accurate API surface including health check and Socket.IO events ✅
|
||||
- [ ] **Deployment Guide**: Create a detailed deployment guide including reverse proxy setup (Nginx/Caddy).
|
||||
|
||||
## Pre-Beta Checklist
|
||||
- [x] All 21 tests pass ✅
|
||||
- [x] Health check endpoint available at `/health` ✅
|
||||
- [x] REST endpoints implemented: `/api/user/:socketId/credits`, `/api/user/:socketId/mode`, `/api/user/:socketId/address` ✅
|
||||
- [ ] **TLS/HTTPS**: Configure reverse proxy (Nginx/Caddy) with SSL certificates before exposing to real users.
|
||||
- [ ] **Database Backups**: Implement automated backup strategy for PostgreSQL.
|
||||
- [ ] **Manual Integration Test**: Run full payment flow with real `wownero-wallet-rpc` (not mocked).
|
||||
- [ ] **Rate Limit Tuning**: Review rate limits for production traffic patterns.
|
||||
- [ ] **Log Rotation**: Ensure logs don't fill disk in production.
|
||||
|
||||
+113
-2
@@ -92,10 +92,121 @@ app.get('/api/payment/status/:paymentId', asyncHandler(async (req, res) => {
|
||||
throw restNotImplemented();
|
||||
}));
|
||||
|
||||
app.get('/api/user/:userId/credits', asyncHandler(async (req, res) => {
|
||||
throw restNotImplemented();
|
||||
app.get('/api/user/:socketId/credits', asyncHandler(async (req, res) => {
|
||||
const { socketId } = req.params;
|
||||
if (!socketId) {
|
||||
throw new ValidationError('Missing socketId', {
|
||||
safeMessage: 'socketId parameter is required.'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await gameModeManager.getOrCreateUser(socketId);
|
||||
res.json({
|
||||
socketId,
|
||||
credits: user.credits || 0,
|
||||
totalCreditsPurchased: user.total_credits_purchased || 0
|
||||
});
|
||||
} catch (error) {
|
||||
throw new AppError('Failed to retrieve credits', {
|
||||
statusCode: 500,
|
||||
safeMessage: 'Unable to retrieve credit balance.',
|
||||
cause: error
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
app.get('/api/user/:socketId/mode', asyncHandler(async (req, res) => {
|
||||
const { socketId } = req.params;
|
||||
if (!socketId) {
|
||||
throw new ValidationError('Missing socketId', {
|
||||
safeMessage: 'socketId parameter is required.'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await gameModeManager.getOrCreateUser(socketId);
|
||||
res.json({
|
||||
socketId,
|
||||
preferredPaymentMode: user.preferred_payment_mode || 'direct',
|
||||
hasPayoutAddress: !!user.payout_address,
|
||||
paymentsEnabled: gameModeManager.paymentsEnabled,
|
||||
directModeEnabled: gameModeManager.directModeEnabled,
|
||||
creditsModeEnabled: gameModeManager.creditsModeEnabled
|
||||
});
|
||||
} catch (error) {
|
||||
throw new AppError('Failed to retrieve user mode', {
|
||||
statusCode: 500,
|
||||
safeMessage: 'Unable to retrieve user mode.',
|
||||
cause: error
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
app.post('/api/user/:socketId/address', asyncHandler(async (req, res) => {
|
||||
const { socketId } = req.params;
|
||||
const { address } = req.body || {};
|
||||
|
||||
if (!socketId) {
|
||||
throw new ValidationError('Missing socketId', {
|
||||
safeMessage: 'socketId parameter is required.'
|
||||
});
|
||||
}
|
||||
|
||||
if (!address || typeof address !== 'string') {
|
||||
throw new ValidationError('Missing or invalid address', {
|
||||
safeMessage: 'A valid payout address is required.'
|
||||
});
|
||||
}
|
||||
|
||||
// Basic address validation (XMR/WOW addresses)
|
||||
const ADDRESS_REGEX = /((?:4|8)[1-9A-HJ-NP-Za-km-z]{90,110}|(?:Wo|WO|ww|WW)[0-9A-Za-z]{88,112}|W[0-9A-Za-z]{90,112})/;
|
||||
if (!ADDRESS_REGEX.test(address.trim())) {
|
||||
throw new ValidationError('Invalid address format', {
|
||||
safeMessage: 'The provided address does not appear to be a valid XMR/WOW address.'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const success = await gameModeManager.setUserPayoutAddress(socketId, address.trim());
|
||||
if (!success) {
|
||||
throw new AppError('Failed to save address', {
|
||||
statusCode: 500,
|
||||
safeMessage: 'Unable to save payout address.'
|
||||
});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Payout address saved successfully.'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AppError || error instanceof ValidationError) {
|
||||
throw error;
|
||||
}
|
||||
throw new AppError('Failed to save address', {
|
||||
statusCode: 500,
|
||||
safeMessage: 'Unable to save payout address.',
|
||||
cause: error
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (req, res) => {
|
||||
const health = {
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime(),
|
||||
memoryUsage: process.memoryUsage(),
|
||||
gameMode: gameModeManager.gameMode,
|
||||
paymentsEnabled: gameModeManager.paymentsEnabled,
|
||||
walletHealthy: walletRPCService.isHealthy,
|
||||
activeGames: activeGames.size,
|
||||
debugMode: debugManager.getDebugStatus().debugMode
|
||||
};
|
||||
res.json(health);
|
||||
});
|
||||
|
||||
app.get('/api/game-modes', (req, res) => {
|
||||
const config = paymentConfigManager.getConfig();
|
||||
const decimals = Number(config.currency?.decimals ?? 12);
|
||||
|
||||
Reference in New Issue
Block a user