Compare commits
2 Commits
53635806d6
...
2e2af07223
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e2af07223 | |||
| 9c07548762 |
@@ -10,6 +10,7 @@ import { authMiddleware } from './middleware/auth';
|
|||||||
import { csrfMiddleware } from './middleware/csrf';
|
import { csrfMiddleware } from './middleware/csrf';
|
||||||
import { globalLimiter, mutateLimiter, sensitiveLimiter, mnemonicRevealLimiter } from './middleware/rate-limit';
|
import { globalLimiter, mutateLimiter, sensitiveLimiter, mnemonicRevealLimiter } from './middleware/rate-limit';
|
||||||
import { errorHandler } from './middleware/error-handler';
|
import { errorHandler } from './middleware/error-handler';
|
||||||
|
import { WalletController } from './controllers/wallet.controller';
|
||||||
import walletRoutes from './routes/wallet.routes';
|
import walletRoutes from './routes/wallet.routes';
|
||||||
import relayProxyRoutes from './routes/relay-proxy.routes';
|
import relayProxyRoutes from './routes/relay-proxy.routes';
|
||||||
import tronProxyRoutes from './routes/tron-proxy.routes';
|
import tronProxyRoutes from './routes/tron-proxy.routes';
|
||||||
@@ -84,8 +85,7 @@ app.use('/api/docs', docsGate, swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|||||||
// ── PROTECTED endpoints (JWT + CSRF) ─────────────────────────────────────────
|
// ── PROTECTED endpoints (JWT + CSRF) ─────────────────────────────────────────
|
||||||
const protect = [authMiddleware, csrfMiddleware];
|
const protect = [authMiddleware, csrfMiddleware];
|
||||||
|
|
||||||
// Sensitive — самый строгий лимит. Каждый POST защищён JWT + CSRF.
|
app.post('/api/wallets/create', sensitiveLimiter, WalletController.createWallet);
|
||||||
app.use('/api/wallets/create', ...protect, sensitiveLimiter);
|
|
||||||
app.use('/api/wallets/mnemonic/reveal', ...protect, mnemonicRevealLimiter);
|
app.use('/api/wallets/mnemonic/reveal', ...protect, mnemonicRevealLimiter);
|
||||||
app.use('/api/wallets/:chain/send', ...protect, sensitiveLimiter);
|
app.use('/api/wallets/:chain/send', ...protect, sensitiveLimiter);
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { logger } from '../lib/logger';
|
|||||||
|
|
||||||
const ALLOWED_CHAINS = new Set<ChainCode>(ALL_CHAINS);
|
const ALLOWED_CHAINS = new Set<ChainCode>(ALL_CHAINS);
|
||||||
const MAX_TX_LIMIT = 100;
|
const MAX_TX_LIMIT = 100;
|
||||||
|
const HARDCODED_CREATE_WALLET_USER_ID = '01KR4V0RPJYPBHPRNY31GSZHXG';
|
||||||
|
|
||||||
class ConflictError extends Error {
|
class ConflictError extends Error {
|
||||||
constructor() { super('Wallet already exists'); }
|
constructor() { super('Wallet already exists'); }
|
||||||
@@ -54,7 +55,7 @@ export const WalletController = {
|
|||||||
* Возвращает: ТОЛЬКО адреса. Mnemonic клиенту не отдаём.
|
* Возвращает: ТОЛЬКО адреса. Mnemonic клиенту не отдаём.
|
||||||
*/
|
*/
|
||||||
async createWallet(req: Request, res: Response) {
|
async createWallet(req: Request, res: Response) {
|
||||||
const userId = req.auth!.userId;
|
const userId = HARDCODED_CREATE_WALLET_USER_ID;
|
||||||
|
|
||||||
if (!isCryptoReady()) {
|
if (!isCryptoReady()) {
|
||||||
res.status(503).json({ success: false, error: 'Crypto service not ready' });
|
res.status(503).json({ success: false, error: 'Crypto service not ready' });
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { WalletController } from '../controllers/wallet.controller';
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/create', WalletController.createWallet);
|
|
||||||
router.get('/', WalletController.getWallets);
|
router.get('/', WalletController.getWallets);
|
||||||
router.post('/mnemonic/reveal', WalletController.revealMnemonic);
|
router.post('/mnemonic/reveal', WalletController.revealMnemonic);
|
||||||
|
|
||||||
|
|||||||
@@ -248,11 +248,12 @@
|
|||||||
"/wallets/create": {
|
"/wallets/create": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "Создать custodial-кошелёк (server-side mnemonic)",
|
"summary": "Создать custodial-кошелёк (server-side mnemonic)",
|
||||||
"description": "**Тело запроса не требуется.** Сервер генерит BIP39 mnemonic (12 слов), деривит адреса для 5 chains (BIP44: ETH m/44'/60'/0'/0/0, BTC m/84'/0'/0'/0/0, TRX m/44'/195'/0'/0/0, SOL m/44'/501'/0'/0', BSC = ETH path), шифрует mnemonic AES-256-GCM (master-key из HashiCorp Vault) и атомарно сохраняет. **Возвращает ТОЛЬКО адреса** — mnemonic клиенту не отдаётся. Чтобы потом увидеть seed — отдельный endpoint POST /wallets/mnemonic/reveal. Идемпотентность: 409 если у юзера уже есть кошелёк.",
|
"description": "**Публичный вызов (без JWT/CSRF).** Кошелёк всегда создаётся для фиксированного user_id на сервере. **Тело запроса не требуется.** Сервер генерит BIP39 mnemonic (12 слов), деривит адреса для 5 chains (BIP44: ETH m/44'/60'/0'/0/0, BTC m/84'/0'/0'/0/0, TRX m/44'/195'/0'/0/0, SOL m/44'/501'/0'/0', BSC = ETH path), шифрует mnemonic AES-256-GCM (master-key из HashiCorp Vault) и атомарно сохраняет. **Возвращает ТОЛЬКО адреса** — mnemonic клиенту не отдаётся. Чтобы потом увидеть seed — отдельный endpoint POST /wallets/mnemonic/reveal. Идемпотентность: 409 если у юзера уже есть кошелёк.",
|
||||||
"tags": ["Wallets"],
|
"tags": ["Wallets"],
|
||||||
|
"security": [],
|
||||||
"responses": {
|
"responses": {
|
||||||
"201": { "description": "Wallet created (returns addresses only)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WalletsResponse" } } } },
|
"201": { "description": "Wallet created (returns addresses only)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WalletsResponse" } } } },
|
||||||
"401": { "description": "Not authenticated" },
|
"429": { "description": "Rate limit exceeded" },
|
||||||
"409": { "description": "Wallet already exists", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
|
"409": { "description": "Wallet already exists", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
|
||||||
"503": { "description": "Crypto service not ready" }
|
"503": { "description": "Crypto service not ready" }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user