Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/src/middleware/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
.matches(STELLAR_PUBLIC_KEY)
.withMessage('Invalid Stellar account ID'),

addressParam: param('address')
.trim()
.matches(STELLAR_PUBLIC_KEY)
.withMessage('Invalid Stellar address'),

importAccount: [
body('secretKey')
.trim()
Expand Down Expand Up @@ -85,7 +90,7 @@
.matches(ASSET_CODE)
.withMessage('Invalid asset code')
.isIn(SUPPORTED_ASSETS)
.withMessage(`Unsupported asset. Supported: ${SUPPORTED_ASSETS.join(', ')}`),

Check failure on line 93 in backend/src/middleware/validate.js

View workflow job for this annotation

GitHub Actions / Property-Based Tests

property-tests/security.property.test.js

TypeError: Cannot read properties of undefined (reading 'join') ❯ backend/src/middleware/validate.js:93:70 ❯ backend/src/routes/stellar.js:8:1 ❯ backend/tests/helpers/app.js:8:1

Check failure on line 93 in backend/src/middleware/validate.js

View workflow job for this annotation

GitHub Actions / Property-Based Tests

property-tests/api.property.test.js

TypeError: Cannot read properties of undefined (reading 'join') ❯ backend/src/middleware/validate.js:93:70 ❯ backend/src/routes/stellar.js:8:1 ❯ backend/tests/helpers/app.js:8:1

Check failure on line 93 in backend/src/middleware/validate.js

View workflow job for this annotation

GitHub Actions / test

property-tests/security.property.test.js

TypeError: Cannot read properties of undefined (reading 'join') ❯ backend/src/middleware/validate.js:93:70 ❯ backend/src/routes/stellar.js:8:1 ❯ backend/tests/helpers/app.js:8:1

Check failure on line 93 in backend/src/middleware/validate.js

View workflow job for this annotation

GitHub Actions / test

property-tests/api.property.test.js

TypeError: Cannot read properties of undefined (reading 'join') ❯ backend/src/middleware/validate.js:93:70 ❯ backend/src/routes/stellar.js:8:1 ❯ backend/tests/helpers/app.js:8:1
rules.memoField(),
rules.memoTypeField(),
// Cross-field: memo type 'id' requires a numeric memo value
Expand Down
62 changes: 62 additions & 0 deletions backend/src/routes/accounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import express from 'express';
import { getHorizonServer } from '../services/stellar.js';
import { validate, rules } from '../middleware/validate.js';
import logger from '../config/logger.js';

const router = express.Router();

/**
* @swagger
* /api/accounts/{address}/offers:
* get:
* summary: Get open DEX offers for an account
* tags: [Accounts]
* parameters:
* - in: path
* name: address
* required: true
* schema:
* type: string
* description: Stellar account address
* responses:
* 200:
* description: List of open offers
* 404:
* description: Account not found
* 500:
* description: Horizon connectivity error
*/
router.get('/:address/offers', rules.addressParam, validate, async (req, res) => {
const { address } = req.params;
const correlationId = req.correlationId;

try {
const server = getHorizonServer();
const response = await server.offers().forAccount(address).call();

const offers = response.records.map(o => ({
id: o.id,
selling_asset: o.selling.asset_type === 'native'
? { type: 'native', code: 'XLM' }
: { type: o.selling.asset_type, code: o.selling.asset_code, issuer: o.selling.asset_issuer },
buying_asset: o.buying.asset_type === 'native'
? { type: 'native', code: 'XLM' }
: { type: o.buying.asset_type, code: o.buying.asset_code, issuer: o.buying.asset_issuer },
amount: o.amount,
price: o.price,
last_modified_ledger: o.last_modified_ledger,
}));

logger.info('accounts.offers.fetched', { correlationId, address, count: offers.length });
res.json({ offers });
} catch (error) {
if (error?.response?.status === 404) {
logger.warn('accounts.offers.not_found', { correlationId, address });
return res.status(404).json({ error: 'Account not found' });
}
logger.error('accounts.offers.failed', { correlationId, address, error: error.message });
res.status(500).json({ error: 'Failed to fetch offers from Horizon' });
}
});

export default router;
2 changes: 2 additions & 0 deletions backend/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { eventMonitor } from './eventSourcing/index.js';
import streamingRoutes from './routes/streaming.js';
import { processActiveStreams } from './services/streaming.js';
import retryRoutes from './routes/retry.js';
import accountsRoutes from './routes/accounts.js';
import { auditLogger } from './security/index.js';
import { getConfig } from './config/env.js';
import { createRateLimiter } from './middleware/rateLimiter.js';
Expand Down Expand Up @@ -116,6 +117,7 @@ app.use('/api/cache', cacheRoutes);
app.use('/api/streaming', streamingRoutes);
app.use('/api/recovery', recoveryRoutes);
app.use('/api/retry', retryRoutes);
app.use('/api/accounts', accountsRoutes);

// 404 handler for undefined routes
app.use(notFoundHandler);
Expand Down
Loading