531 lines
12 KiB
TypeScript
531 lines
12 KiB
TypeScript
/**
|
|
* Investment Controller
|
|
* Handles investment-related endpoints
|
|
*/
|
|
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import { productService, RiskProfile } from '../services/product.service';
|
|
import { accountService, CreateAccountInput } from '../services/account.service';
|
|
import {
|
|
transactionService,
|
|
TransactionType,
|
|
TransactionStatus,
|
|
WithdrawalStatus,
|
|
} from '../services/transaction.service';
|
|
|
|
// ============================================================================
|
|
// Types
|
|
// ============================================================================
|
|
|
|
// Use Request directly - user is already declared globally in auth.middleware.ts
|
|
type AuthRequest = Request;
|
|
|
|
// ============================================================================
|
|
// Product Controllers
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get all investment products
|
|
*/
|
|
export async function getProducts(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { riskProfile } = req.query;
|
|
|
|
let products;
|
|
if (riskProfile) {
|
|
products = await productService.getProductsByRiskProfile(riskProfile as RiskProfile);
|
|
} else {
|
|
products = await productService.getProducts();
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: products,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get product by ID
|
|
*/
|
|
export async function getProductById(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { productId } = req.params;
|
|
|
|
const product = await productService.getProductById(productId);
|
|
if (!product) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: { message: 'Product not found', code: 'NOT_FOUND' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Get additional stats
|
|
const stats = await productService.getProductStats(productId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { ...product, stats },
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get product performance
|
|
*/
|
|
export async function getProductPerformance(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { productId } = req.params;
|
|
const { period = 'month' } = req.query;
|
|
|
|
const performance = await productService.getProductPerformance(
|
|
productId,
|
|
period as 'week' | 'month' | '3months' | 'year'
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: performance,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Account Controllers
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get user accounts
|
|
*/
|
|
export async function getUserAccounts(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const accounts = await accountService.getUserAccounts(userId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: accounts,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get account summary
|
|
*/
|
|
export async function getAccountSummary(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const summary = await accountService.getAccountSummary(userId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: summary,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get account by ID
|
|
*/
|
|
export async function getAccountById(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { accountId } = req.params;
|
|
|
|
const account = await accountService.getAccountById(accountId);
|
|
if (!account) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: { message: 'Account not found', code: 'NOT_FOUND' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (account.userId !== userId) {
|
|
res.status(403).json({
|
|
success: false,
|
|
error: { message: 'Forbidden', code: 'FORBIDDEN' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Get performance history
|
|
const performance = await accountService.getAccountPerformance(accountId, 30);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { ...account, performance },
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create investment account
|
|
*/
|
|
export async function createAccount(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { productId, initialDeposit } = req.body;
|
|
|
|
if (!productId || !initialDeposit) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: { message: 'Product ID and initial deposit are required', code: 'VALIDATION_ERROR' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const input: CreateAccountInput = {
|
|
userId,
|
|
productId,
|
|
initialDeposit: Number(initialDeposit),
|
|
};
|
|
|
|
const account = await accountService.createAccount(input);
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: account,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close account
|
|
*/
|
|
export async function closeAccount(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { accountId } = req.params;
|
|
|
|
const account = await accountService.getAccountById(accountId);
|
|
if (!account) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: { message: 'Account not found', code: 'NOT_FOUND' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (account.userId !== userId) {
|
|
res.status(403).json({
|
|
success: false,
|
|
error: { message: 'Forbidden', code: 'FORBIDDEN' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const closedAccount = await accountService.closeAccount(accountId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: closedAccount,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Transaction Controllers
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get account transactions
|
|
*/
|
|
export async function getTransactions(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { accountId } = req.params;
|
|
const { type, status, limit = 50, offset = 0 } = req.query;
|
|
|
|
const account = await accountService.getAccountById(accountId);
|
|
if (!account) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: { message: 'Account not found', code: 'NOT_FOUND' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (account.userId !== userId) {
|
|
res.status(403).json({
|
|
success: false,
|
|
error: { message: 'Forbidden', code: 'FORBIDDEN' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { transactions, total } = await transactionService.getAccountTransactions(accountId, {
|
|
type: type as TransactionType | undefined,
|
|
status: status as TransactionStatus | undefined,
|
|
limit: Number(limit),
|
|
offset: Number(offset),
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
data: transactions,
|
|
pagination: {
|
|
total,
|
|
limit: Number(limit),
|
|
offset: Number(offset),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create deposit
|
|
*/
|
|
export async function createDeposit(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { accountId } = req.params;
|
|
const { amount } = req.body;
|
|
|
|
const account = await accountService.getAccountById(accountId);
|
|
if (!account) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: { message: 'Account not found', code: 'NOT_FOUND' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (account.userId !== userId) {
|
|
res.status(403).json({
|
|
success: false,
|
|
error: { message: 'Forbidden', code: 'FORBIDDEN' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!amount || amount <= 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: { message: 'Valid amount is required', code: 'VALIDATION_ERROR' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const transaction = await transactionService.createDeposit({
|
|
accountId,
|
|
amount: Number(amount),
|
|
});
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: transaction,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create withdrawal request
|
|
*/
|
|
export async function createWithdrawal(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { accountId } = req.params;
|
|
const { amount, bankInfo, cryptoInfo } = req.body;
|
|
|
|
const account = await accountService.getAccountById(accountId);
|
|
if (!account) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: { message: 'Account not found', code: 'NOT_FOUND' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (account.userId !== userId) {
|
|
res.status(403).json({
|
|
success: false,
|
|
error: { message: 'Forbidden', code: 'FORBIDDEN' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!amount || amount <= 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: { message: 'Valid amount is required', code: 'VALIDATION_ERROR' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const withdrawal = await transactionService.createWithdrawal(userId, {
|
|
accountId,
|
|
amount: Number(amount),
|
|
bankInfo,
|
|
cryptoInfo,
|
|
});
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: withdrawal,
|
|
message: 'Withdrawal request submitted. Processing time: 72 hours.',
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user withdrawals
|
|
*/
|
|
export async function getWithdrawals(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { status } = req.query;
|
|
|
|
const withdrawals = await transactionService.getUserWithdrawals(
|
|
userId,
|
|
status as WithdrawalStatus | undefined
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: withdrawals,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get account distributions
|
|
*/
|
|
export async function getDistributions(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { accountId } = req.params;
|
|
|
|
const account = await accountService.getAccountById(accountId);
|
|
if (!account) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: { message: 'Account not found', code: 'NOT_FOUND' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (account.userId !== userId) {
|
|
res.status(403).json({
|
|
success: false,
|
|
error: { message: 'Forbidden', code: 'FORBIDDEN' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const distributions = await transactionService.getAccountDistributions(accountId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: distributions,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|