// ============================================================================
// FOCUS360 FULL-STACK MONOLITHIC FILE SETUP
// Save this file as setup.js and run: node setup.js
// It will generate the complete folder structure and all code files automatically.
// ============================================================================
const fs = require('fs');
const path = require('path');
const files = {
// --------------------------------------------------------------------------
// BACKEND FILES
// --------------------------------------------------------------------------
'backend/package.json': JSON.stringify({
"name": "focus360-backend",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.2.0"
}
}, null, 2),
'backend/.env': `PORT=5000\nMONGO_URI=mongodb://localhost:27017/focus360\nJWT_SECRET=focus360_super_secret_jwt_key\n`,
'backend/models/User.js': `const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('User', userSchema);
`,
'backend/models/Trade.js': `const mongoose = require('mongoose');
const tradeSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
symbol: { type: String, required: true },
type: { type: String, enum: ['BUY', 'SELL'], required: true },
entryPrice: { type: Number, required: true },
exitPrice: { type: Number, default: null },
pnl: { type: Number, default: 0 },
status: { type: String, enum: ['OPEN', 'CLOSED'], default: 'OPEN' },
createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Trade', tradeSchema);
`,
'backend/middleware/auth.js': `const jwt = require('jsonwebtoken');
module.exports = function (req, res, next) {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) return res.status(401).json({ message: 'Access denied. No token provided.' });
try {
const verified = jwt.verify(token, process.env.JWT_SECRET);
req.user = verified;
next();
} catch (err) {
res.status(400).json({ message: 'Invalid token.' });
}
};
`,
'backend/server.js': `const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const User = require('./models/User');
const Trade = require('./models/Trade');
const auth = require('./middleware/auth');
const app = express();
app.use(express.json());
app.use(cors());
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log('MongoDB Connected'))
.catch(err => console.error('DB Error:', err));
app.post('/api/auth/register', async (req, res) => {
try {
const { email, password } = req.body;
let user = await User.findOne({ email });
if (user) return res.status(400).json({ message: 'User already exists' });
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
user = new User({ email, password: hashedPassword });
await user.save();
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
res.json({ token, user: { id: user._id, email: user.email } });
} catch (err) {
res.status(500).json({ message: 'Server error' });
}
});
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) return res.status(400).json({ message: 'Invalid credentials' });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
res.json({ token, user: { id: user._id, email: user.email } });
} catch (err) {
res.status(500).json({ message: 'Server error' });
}
});
app.get('/api/trades', auth, async (req, res) => {
try {
const trades = await Trade.find({ userId: req.user.id }).sort({ createdAt: -1 });
res.json(trades);
} catch (err) {
res.status(500).json({ message: 'Error fetching trades' });
}
});
app.post('/api/trades', auth, async (req, res) => {
try {
const { symbol, type, entryPrice, exitPrice, pnl, status } = req.body;
const newTrade = new Trade({
userId: req.user.id,
symbol,
type,
entryPrice,
exitPrice,
pnl,
status
});
await newTrade.save();
res.json(newTrade);
} catch (err) {
res.status(500).json({ message: 'Error saving trade' });
}
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(\`Focus360 Server running on port \${PORT}\`));
`,
// --------------------------------------------------------------------------
// FRONTEND FILES
// --------------------------------------------------------------------------
'frontend/package.json': JSON.stringify({
"name": "focus360-frontend",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"@react-native-async-storage/async-storage": "~1.21.0",
"expo": "~50.0.0",
"react": "18.2.0",
"react-native": "0.73.4",
"react-native-svg": "14.1.0"
}
}, null, 2),
'frontend/context/AuthContext.js': `import React, { createContext, useState, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [userToken, setUserToken] = useState(null);
const [loading, setLoading] = useState(true);
const API_URL = 'http://localhost:5000/api/auth';
useEffect(() => {
const loadToken = async () => {
const token = await AsyncStorage.getItem('userToken');
setUserToken(token);
setLoading(false);
};
loadToken();
}, []);
const login = async (email, password) => {
const res = await fetch(\`\${API_URL}/login\`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
if (data.token) {
await AsyncStorage.setItem('userToken', data.token);
setUserToken(data.token);
} else {
throw new Error(data.message || 'Login failed');
}
};
const logout = async () => {
await AsyncStorage.removeItem('userToken');
setUserToken(null);
};
return (
{children}
);
};
`,
'frontend/components/PnlChart.js': `import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import Svg, { Polyline, Line } from 'react-native-svg';
export default function PnlChart({ trades }) {
if (!trades || trades.length === 0) {
return No trade data available for chart ;
}
let cumulativePnl = 0;
const pnlPoints = trades.map((t, idx) => {
cumulativePnl += t.pnl || 0;
const x = (idx / Math.max(trades.length - 1, 1)) * 300;
const y = 100 - Math.min(Math.max(cumulativePnl, -100), 100);
return \`\${x},\${y}\`;
}).join(' ');
return (
Cumulative Performance
);
}
const styles = StyleSheet.create({
container: { backgroundColor: '#1A1D24', padding: 16, borderRadius: 8, alignItems: 'center', marginVertical: 10 },
title: { color: '#FFF', fontWeight: 'bold', marginBottom: 10 },
emptyText: { color: '#888', textAlign: 'center', marginVertical: 20 }
});
`,
'frontend/screens/LoginScreen.js': `import React, { useState, useContext } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert } from 'react-native';
import { AuthContext } from '../context/AuthContext';
export default function LoginScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { login } = useContext(AuthContext);
const handleLogin = async () => {
try {
await login(email, password);
} catch (err) {
Alert.alert('Error', err.message);
}
};
return (
FOCUS 360
SIGN IN
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0D0F12', justifyContent: 'center', padding: 24 },
logo: { fontSize: 32, fontWeight: '900', color: '#00E676', textAlign: 'center', marginBottom: 40, letterSpacing: 2 },
input: { backgroundColor: '#1A1D24', color: '#FFF', padding: 16, borderRadius: 8, marginBottom: 16 },
button: { backgroundColor: '#00E676', padding: 16, borderRadius: 8, alignItems: 'center', marginTop: 10 },
buttonText: { color: '#000', fontWeight: 'bold', fontSize: 16 }
});
`,
'frontend/screens/DashboardScreen.js': `import React, { useState, useEffect, useContext } from 'react';
import { View, Text, FlatList, TouchableOpacity, StyleSheet } from 'react-native';
import { AuthContext } from '../context/AuthContext';
import AsyncStorage from '@react-native-async-storage/async-storage';
import PnlChart from '../components/PnlChart';
export default function DashboardScreen({ onNavigate }) {
const { logout } = useContext(AuthContext);
const [trades, setTrades] = useState([]);
useEffect(() => {
fetchTrades();
}, []);
const fetchTrades = async () => {
const token = await AsyncStorage.getItem('userToken');
const res = await fetch('http://localhost:5000/api/trades', {
headers: { Authorization: \`Bearer \${token}\` }
});
const data = await res.json();
if (Array.isArray(data)) setTrades(data);
};
return (
Focus360 Dashboard
Logout
onNavigate('TradeEntry')}>
+ Log New Trade
item._id}
renderItem={({ item }) => (
{item.symbol} ({item.type})
Entry: \${item.entryPrice}
= 0 ? '#00E676' : '#FF5252' }]}>
{item.pnl >= 0 ? \`+\$\${item.pnl}\` : \`-\$\${Math.abs(item.pnl)}\`}
)}
/>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0D0F12', padding: 20, paddingTop: 50 },
header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 },
headerTitle: { color: '#FFF', fontSize: 20, fontWeight: 'bold' },
logout: { color: '#FF5252' },
addBtn: { backgroundColor: '#00E676', padding: 12, borderRadius: 6, alignItems: 'center', marginVertical: 15 },
addBtnText: { color: '#000', fontWeight: 'bold' },
tradeCard: { backgroundColor: '#1A1D24', padding: 16, borderRadius: 8, flexDirection: 'row', justifyContent: 'space-between', marginBottom: 10 },
symbol: { color: '#FFF', fontWeight: 'bold', fontSize: 16 },
details: { color: '#888', fontSize: 12, marginTop: 4 },
pnl: { fontWeight: 'bold', fontSize: 16 }
});
`,
'frontend/screens/TradeEntryScreen.js': `import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function TradeEntryScreen({ onBack }) {
const [symbol, setSymbol] = useState('');
const [type, setType] = useState('BUY');
const [entryPrice, setEntryPrice] = useState('');
const [pnl, setPnl] = useState('');
const handleSave = async () => {
if (!symbol || !entryPrice) {
Alert.alert('Missing Info', 'Please enter a symbol and entry price');
return;
}
const token = await AsyncStorage.getItem('userToken');
const res = await fetch('http://localhost:5000/api/trades', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: \`Bearer \${token}\`
},
body: JSON.stringify({
symbol: symbol.toUpperCase(),
type,
entryPrice: parseFloat(entryPrice),
pnl: parseFloat(pnl) || 0,
status: 'CLOSED'
})
});
if (res.ok) {
onBack();
} else {
Alert.alert('Error', 'Failed to save trade');
}
};
return (
← Back
Log Trade
setType('BUY')}>
BUY
setType('SELL')}>
SELL
Save Trade
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0D0F12', padding: 20, paddingTop: 50 },
back: { color: '#00E676', fontSize: 16, marginBottom: 20 },
title: { color: '#FFF', fontSize: 24, fontWeight: 'bold', marginBottom: 20 },
input: { backgroundColor: '#1A1D24', color: '#FFF', padding: 16, borderRadius: 8, marginBottom: 12 },
typeContainer: { flexDirection: 'row', gap: 10, marginVertical: 12 },
typeBtn: { flex: 1, padding: 14, backgroundColor: '#1A1D24', borderRadius: 8, alignItems: 'center' },
selectedBuy: { backgroundColor: '#00E676' },
selectedSell: { backgroundColor: '#FF5252' },
typeText: { color: '#FFF', fontWeight: 'bold' },
saveBtn: { backgroundColor: '#00E676', padding: 16, borderRadius: 8, alignItems: 'center', marginTop: 20 },
saveText: { color: '#000', fontWeight: 'bold', fontSize: 16 }
});
`,
'frontend/App.js': `import React, { useContext, useState } from 'react';
import { AuthProvider, AuthContext } from './context/AuthContext';
import LoginScreen from './screens/LoginScreen';
import DashboardScreen from './screens/DashboardScreen';
import TradeEntryScreen from './screens/TradeEntryScreen';
function MainNavigator() {
const { userToken } = useContext(AuthContext);
const [screen, setScreen] = useState('Dashboard');
if (!userToken) {
return ;
}
if (screen === 'TradeEntry') {
return setScreen('Dashboard')} />;
}
return ;
}
export default function App() {
return (
);
}
`
};
// Automatic extraction execution block
Object.entries(files).forEach(([filePath, content]) => {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, content, 'utf8');
console.log(`Created: ${filePath}`);
});
console.log('\nFocus360 Codebase Setup Complete!');