diff --git a/backend/src/controllers/user.controller.ts b/backend/src/controllers/user.controller.ts index ae31d17c..d47d3bc0 100644 --- a/backend/src/controllers/user.controller.ts +++ b/backend/src/controllers/user.controller.ts @@ -39,7 +39,13 @@ export const registerUser = async (req: Request, res: Response, next: NextFuncti */ export const getUser = async (req: Request, res: Response, next: NextFunction) => { try { - const publicKey = req.params.publicKey as string; + const { publicKey } = req.params; + if (typeof publicKey !== 'string') { + return res.status(400).json({ error: 'Invalid publicKey parameter' }); + } + if (!/^G[A-Z2-7]{55}$/.test(publicKey)) { + return res.status(400).json({ error: 'Invalid Stellar public key format' }); + } const user = await prisma.user.findUnique({ where: { publicKey }, diff --git a/backend/tests/user.controller.test.ts b/backend/tests/user.controller.test.ts index c2c6128b..797eb0ab 100644 --- a/backend/tests/user.controller.test.ts +++ b/backend/tests/user.controller.test.ts @@ -75,8 +75,26 @@ describe('User Controller', () => { }); describe('getUser', () => { + it('should return 400 if publicKey is missing', async () => { + req.params = {}; + + await getUser(req as Request, res as Response, next); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ error: 'Invalid publicKey parameter' }); + }); + + it('should return 400 if publicKey is malformed', async () => { + req.params = { publicKey: 'invalid-key' }; + + await getUser(req as Request, res as Response, next); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ error: 'Invalid Stellar public key format' }); + }); + it('should return 404 if user not found', async () => { - req.params = { publicKey: 'GNOTFOUND' }; + req.params = { publicKey: 'GD2XP6FNWL6IWULVMPNA2RV2T7GLCJHK3RH75GBCY7TSVIWDITJN4FXJ' }; (prisma.user.findUnique as any).mockResolvedValue(null); await getUser(req as Request, res as Response, next); @@ -85,8 +103,9 @@ describe('User Controller', () => { }); it('should return user if found', async () => { - req.params = { publicKey: 'GUSER1' }; - const mockUser = { publicKey: 'GUSER1', sentStreams: [], receivedStreams: [] }; + const publicKey = 'GD2XP6FNWL6IWULVMPNA2RV2T7GLCJHK3RH75GBCY7TSVIWDITJN4FXJ'; + req.params = { publicKey }; + const mockUser = { publicKey, sentStreams: [], receivedStreams: [] }; (prisma.user.findUnique as any).mockResolvedValue(mockUser); await getUser(req as Request, res as Response, next); diff --git a/frontend/src/components/FAQ.tsx b/frontend/src/components/FAQ.tsx index 195d6a4b..24e86b90 100644 --- a/frontend/src/components/FAQ.tsx +++ b/frontend/src/components/FAQ.tsx @@ -8,7 +8,7 @@ const faqs = [ }, { q: 'Which chains do you support?', - a: 'We are currently live on Ethereum Mainnet, Arbitrum, and Optimism. Polygon and Base support is coming in Q3 2026.', + a: 'FlowFi is built on Stellar using Soroban smart contracts. We currently run on Stellar Testnet, with mainnet support planned as the protocol matures.', }, { q: 'Can recipients withdraw at any time?', diff --git a/frontend/src/components/Hero.tsx b/frontend/src/components/Hero.tsx index 1d33f846..42d12833 100644 --- a/frontend/src/components/Hero.tsx +++ b/frontend/src/components/Hero.tsx @@ -1,4 +1,5 @@ +import Link from 'next/link'; import { Button } from './ui/Button'; import { MobileMockup } from './MobileMockup'; @@ -26,15 +27,14 @@ export const Hero = () => {

- - + + +
diff --git a/frontend/src/components/Stats.tsx b/frontend/src/components/Stats.tsx index e5dc86a0..d7d6e205 100644 --- a/frontend/src/components/Stats.tsx +++ b/frontend/src/components/Stats.tsx @@ -1,9 +1,9 @@ const stats = [ - { label: 'Total Value Locked', value: '$240M+', gradient: 'text-gradient' }, - { label: 'Active Streams', value: '85k+', gradient: 'text-white' }, - { label: 'Network Savings', value: '$4.2M', gradient: 'text-gradient-secondary' }, + { label: 'Network', value: 'Stellar Testnet', gradient: 'text-gradient' }, + { label: 'Status', value: 'Early Access', gradient: 'text-white' }, + { label: 'Settlement', value: 'Real-time', gradient: 'text-gradient-secondary' }, ];