diff --git a/backend/src/routes/proxy.js b/backend/src/routes/proxy.js
index 15400f5..4f4e34a 100644
--- a/backend/src/routes/proxy.js
+++ b/backend/src/routes/proxy.js
@@ -4,300 +4,211 @@ const axios = require('axios');
const Helpers = require('../helpers');
const qs = require('qs');
+/**
+ * Super Stealth Proxy v3
+ * Engineered for high-fidelity dynamic site replication (YouTube, Google, etc.)
+ */
+
+const PROXY_PATH = '/api/proxy';
+
const proxyHandler = Helpers.wrapAsync(async (req, res) => {
let targetUrl = req.query.url;
-
- if (targetUrl) {
- try {
- const urlObj = new URL(targetUrl);
- Object.keys(req.query).forEach(key => {
- if (key !== 'url') {
- urlObj.searchParams.set(key, req.query[key]);
- }
- });
- targetUrl = urlObj.href;
- } catch (e) {}
- }
- // Improved Path-based and Referer detection
+ // 1. URL Resolution
if (!targetUrl || !targetUrl.startsWith('http')) {
const originalUrl = req.originalUrl;
-
- // Check if URL is in the path directly (e.g. /api/proxy/https://...)
- const pathMarker = '/api/proxy/';
- if (originalUrl.includes(pathMarker)) {
- const parts = originalUrl.split(pathMarker);
- const remainder = parts[1];
- if (remainder && remainder.startsWith('http')) {
- targetUrl = remainder;
- } else if (req.headers.referer && req.headers.referer.includes('url=')) {
- // Smart reconstruction from Referer
- try {
- const refUrl = new URL(req.headers.referer);
- const parentTarget = refUrl.searchParams.get('url');
- if (parentTarget) {
- // Reconstruct: parent origin + current query/path
- const currentPath = req.url.replace('/api/proxy', '');
- targetUrl = new URL(currentPath, parentTarget).href;
- }
- } catch (e) {}
- }
+ if (originalUrl.includes(PROXY_PATH + '/')) {
+ targetUrl = originalUrl.split(PROXY_PATH + '/')[1];
+ } else if (req.headers.referer && req.headers.referer.includes(PROXY_PATH)) {
+ try {
+ const refUrl = new URL(req.headers.referer);
+ const parentTarget = refUrl.searchParams.get('url');
+ if (parentTarget) {
+ const relativePath = req.url.replace(PROXY_PATH, '');
+ targetUrl = new URL(relativePath, parentTarget).href;
+ }
+ } catch (e) {}
}
}
if (!targetUrl || !targetUrl.startsWith('http')) {
- return res.status(400).send(`
-
-
URL Required
-
The stealth proxy needs a target URL to work.
-
Go Back
-
- `);
+ return res.status(400).send('Invalid target URL.');
}
try {
- const method = req.method;
const headers = {};
-
- const forwardHeaders = [
- 'accept', 'accept-language', 'accept-encoding', 'user-agent', 'content-type', 'range', 'authorization', 'x-requested-with'
- ];
-
- forwardHeaders.forEach(h => {
- if (req.headers[h]) {
- headers[h] = req.headers[h];
- }
- });
+ const forwardHeaders = ['accept', 'accept-language', 'range', 'authorization', 'content-type'];
+ forwardHeaders.forEach(h => { if (req.headers[h]) headers[h] = req.headers[h]; });
- if (!headers['user-agent']) {
- headers['user-agent'] = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1';
- }
-
- delete headers['referer'];
- delete headers['origin'];
+ // Force Desktop User-Agent
+ headers['user-agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36';
+
+ try {
+ const targetObj = new URL(targetUrl);
+ headers['referer'] = targetObj.origin + '/';
+ headers['origin'] = targetObj.origin;
+ headers['host'] = targetObj.host;
+ } catch(e) {}
const axiosConfig = {
url: targetUrl,
- method: method,
+ method: req.method,
headers: headers,
responseType: 'arraybuffer',
- validateStatus: () => true,
- maxRedirects: 10,
+ validateStatus: () => true,
+ maxRedirects: 5,
timeout: 30000,
+ maxContentLength: Infinity,
+ maxBodyLength: Infinity,
decompress: true
};
- if (method !== 'GET' && method !== 'HEAD' && req.body) {
- if (headers['content-type'] && headers['content-type'].includes('application/x-www-form-urlencoded')) {
- axiosConfig.data = typeof req.body === 'string' ? req.body : qs.stringify(req.body);
- } else {
- axiosConfig.data = req.body;
- }
+ if (req.method !== 'GET' && req.body) {
+ axiosConfig.data = (headers['content-type'] || '').includes('form') ? qs.stringify(req.body) : req.body;
}
const response = await axios(axiosConfig);
- const finalUrl = response.request.res.responseUrl || targetUrl;
const contentType = response.headers['content-type'] || '';
+ const finalUrl = response.request.res ? response.request.res.responseUrl : targetUrl;
+ // Handle Redirects
if (response.status >= 300 && response.status < 400 && response.headers.location) {
const redirUrl = new URL(response.headers.location, finalUrl).href;
- return res.redirect(`/api/proxy?url=${encodeURIComponent(redirUrl)}`);
+ return res.redirect(`${PROXY_PATH}?url=${encodeURIComponent(redirUrl)}`);
}
+ // Strip Security Headers
+ const forbiddenHeaders = ['x-frame-options', 'content-security-policy', 'content-security-policy-report-only', 'strict-transport-security', 'set-cookie'];
Object.keys(response.headers).forEach(key => {
- const lowerKey = key.toLowerCase();
- if (![
- 'x-frame-options',
- 'content-security-policy',
- 'content-security-policy-report-only',
- 'content-length',
- 'transfer-encoding',
- 'connection',
- 'strict-transport-security',
- 'x-content-type-options',
- 'set-cookie',
- 'access-control-allow-origin'
- ].includes(lowerKey)) {
+ if (!forbiddenHeaders.includes(key.toLowerCase())) {
res.setHeader(key, response.headers[key]);
}
});
res.setHeader('Access-Control-Allow-Origin', '*');
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
-
- let data = response.data;
+ res.setHeader('X-Proxy-Target', finalUrl);
if (contentType.includes('text/html')) {
- let html = data.toString('utf-8');
+ let html = response.data.toString('utf-8');
- let origin = '';
- try {
- const parsedUrl = new URL(finalUrl);
- origin = parsedUrl.origin;
- } catch (e) { origin = targetUrl; }
-
- const headInjection = `
-
+ const injection = `
`;
+ html = html.replace(//i, `${injection}`);
- if (html.toLowerCase().includes('')) {
- html = html.replace(//i, `${headInjection}`);
- } else if (html.toLowerCase().includes('')) {
- html = html.replace(//i, `${headInjection}`);
- } else {
- html = headInjection + html;
- }
-
- html = html.replace(/(href|src|action)=["'](.*?)["']/gi, (match, attr, p1) => {
- if (!p1 || p1.startsWith('#') || p1.startsWith('javascript:') || p1.startsWith('data:') || p1.startsWith('mailto:')) return match;
-
- try {
- const absoluteUrl = new URL(p1, finalUrl).href;
- const lowerAttr = attr.toLowerCase();
-
- if (lowerAttr === 'href' || lowerAttr === 'action' || match.toLowerCase().includes('
-
-
- `;
- win.document.open();
- win.document.write(content);
- win.document.close();
- }
- keyPresses = [];
- }
- }
- };
-
- window.addEventListener('keydown', handleKeyDown);
- return () => window.removeEventListener('keydown', handleKeyDown);
- }, []);
-
const handleExit = () => {
setStepsEnabled(false);
};
- const title = 'App Draft'
- const description = "24/7 looped white noise audio player to improve focus during study sessions."
+ const title = 'Balatro App'
+ const description = "A high-fidelity poker-inspired roguelike deck builder."
const url = "https://flatlogic.com/"
const image = "https://flatlogic.com/logo.svg"
const imageWidth = '1920'
@@ -524,4 +192,4 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
)
}
-export default appWithTranslation(MyApp);
+export default appWithTranslation(MyApp);
\ No newline at end of file
diff --git a/frontend/src/pages/browser.tsx b/frontend/src/pages/browser.tsx
new file mode 100644
index 0000000..4e5ebed
--- /dev/null
+++ b/frontend/src/pages/browser.tsx
@@ -0,0 +1,304 @@
+import React, { useState, useRef, useEffect } from 'react';
+import type { ReactElement } from 'react';
+import Head from 'next/head';
+import LayoutGuest from '../layouts/Guest';
+import { getPageTitle } from '../config';
+import BaseIcon from '../components/BaseIcon';
+import { mdiArrowLeft, mdiArrowRight, mdiRefresh, mdiHome, mdiMagnify, mdiShieldCheck, mdiDotsVertical, mdiAlertOctagon, mdiPlus, mdiClose } from '@mdi/js';
+
+const QUICK_LINKS = [
+ { name: 'Google', url: 'https://www.google.com', color: 'bg-blue-500' },
+ { name: 'YouTube', url: 'https://www.youtube.com', color: 'bg-red-500' },
+ { name: 'Wikipedia', url: 'https://www.wikipedia.org', color: 'bg-slate-500' },
+ { name: 'Reddit', url: 'https://www.reddit.com', color: 'bg-orange-500' },
+ { name: 'Twitch', url: 'https://www.twitch.tv', color: 'bg-purple-500' },
+];
+
+interface Tab {
+ id: string;
+ url: string;
+ displayUrl: string;
+ iframeUrl: string;
+ title: string;
+ isLoading: boolean;
+}
+
+export default function BrowserPage() {
+ const [tabs, setTabs] = useState([
+ { id: '1', url: '', displayUrl: '', iframeUrl: '', title: 'Educational Portal', isLoading: false }
+ ]);
+ const [activeTabId, setActiveTabId] = useState('1');
+
+ const activeTab = tabs.find(t => t.id === activeTabId) || tabs[0];
+
+ const getProxyUrl = (target: string) => {
+ const apiBase = process.env.NEXT_PUBLIC_BACK_API || '/api';
+ return `${apiBase}/proxy?url=${encodeURIComponent(target)}`;
+ };
+
+ const updateTab = (id: string, updates: Partial) => {
+ setTabs(prev => prev.map(t => t.id === id ? { ...t, ...updates } : t));
+ };
+
+ const navigate = (target: string, tabId: string = activeTabId) => {
+ let finalTarget = target.trim();
+ if (!finalTarget) return;
+
+ if (!finalTarget.startsWith('http')) {
+ if (finalTarget.includes('.') && !finalTarget.includes(' ')) {
+ finalTarget = 'https://' + finalTarget;
+ } else {
+ finalTarget = `https://www.google.com/search?q=${encodeURIComponent(finalTarget)}`;
+ }
+ }
+
+ updateTab(tabId, {
+ iframeUrl: getProxyUrl(finalTarget),
+ displayUrl: finalTarget,
+ url: finalTarget,
+ isLoading: true,
+ title: 'Loading...'
+ });
+ };
+
+ const addTab = () => {
+ const newId = Math.random().toString(36).substr(2, 9);
+ setTabs(prev => [...prev, {
+ id: newId,
+ url: '',
+ displayUrl: '',
+ iframeUrl: '',
+ title: 'New Tab',
+ isLoading: false
+ }]);
+ setActiveTabId(newId);
+ };
+
+ const closeTab = (e: React.MouseEvent, id: string) => {
+ e.stopPropagation();
+ if (tabs.length === 1) {
+ updateTab(id, { url: '', displayUrl: '', iframeUrl: '', title: 'Educational Portal', isLoading: false });
+ return;
+ }
+ const newTabs = tabs.filter(t => t.id !== id);
+ setTabs(newTabs);
+ if (activeTabId === id) {
+ setActiveTabId(newTabs[newTabs.length - 1].id);
+ }
+ };
+
+ const goHome = () => {
+ updateTab(activeTabId, { iframeUrl: '', url: '', displayUrl: '', title: 'Educational Portal' });
+ };
+
+ const panic = () => {
+ window.location.href = 'https://classroom.google.com';
+ };
+
+ useEffect(() => {
+ const handleMessage = (e: MessageEvent) => {
+ if (e.data && e.data.type === 'proxy-url-change') {
+ const newUrl = e.data.url;
+ try {
+ const hostname = new URL(newUrl).hostname;
+ updateTab(activeTabId, { displayUrl: newUrl, url: newUrl, title: hostname });
+ } catch(e) {
+ updateTab(activeTabId, { displayUrl: newUrl, url: newUrl });
+ }
+ }
+ };
+
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') panic();
+ };
+
+ window.addEventListener('message', handleMessage);
+ window.addEventListener('keydown', handleKeyDown);
+ return () => {
+ window.removeEventListener('message', handleMessage);
+ window.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [activeTabId]);
+
+ return (
+
+
+
SCHOOLWORK - Educational Resources
+
+
+
+ {/* Chrome-style Tab Bar */}
+
+ {tabs.map(tab => (
+
setActiveTabId(tab.id)}
+ className={`
+ rounded-t-lg px-4 py-2 text-xs flex items-center gap-2 min-w-[120px] max-w-[200px] cursor-pointer relative group transition-all
+ ${activeTabId === tab.id ? 'bg-white shadow-sm border-t border-x border-[#bdc1c6] z-10' : 'bg-[#dee1e6] hover:bg-[#cfd2d7] text-slate-600'}
+ `}
+ >
+
+
+ {tab.title}
+
+
closeTab(e, tab.id)}
+ className="hover:bg-gray-200 rounded-full p-0.5 opacity-0 group-hover:opacity-100"
+ >
+
+
+ {activeTabId !== tab.id &&
}
+
+ ))}
+
+
+
+
+
+ {/* Toolbar */}
+
+
+ window.history.back()} className="p-1.5 hover:bg-gray-100 rounded-full transition-colors text-slate-600">
+
+
+ window.history.forward()} className="p-1.5 hover:bg-gray-100 rounded-full transition-colors text-slate-600">
+
+
+ updateTab(activeTabId, { iframeUrl: activeTab.iframeUrl })} className="p-1.5 hover:bg-gray-100 rounded-full transition-colors text-slate-600">
+
+
+
+
+
+
+
+
+
+
+
+
+ PANIC (ESC)
+
+
+
+
+
+
+
+ {/* Browser Content */}
+
+ {tabs.map(tab => (
+
+ {!tab.iframeUrl ? (
+
+
+
+ S
+ t
+ u
+ d
+ y
+ P
+ o
+ r
+ t
+ a
+ l
+
+
+
+
+
+
+
e.key === 'Enter' && navigate((e.target as HTMLInputElement).value, tab.id)}
+ />
+
+
+
+ {QUICK_LINKS.map(link => (
+
navigate(link.url, tab.id)}
+ className="flex flex-col items-center gap-2 group"
+ >
+
+ {link.name[0]}
+
+ {link.name}
+
+ ))}
+
+
+
+ ) : (
+ <>
+ {tab.isLoading && (
+
+ )}
+
+ ))}
+
+
+
+
+ );
+}
+
+BrowserPage.getLayout = function getLayout(page: ReactElement) {
+ return {page} ;
+};
\ No newline at end of file
diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx
index 597c478..5775bde 100644
--- a/frontend/src/pages/index.tsx
+++ b/frontend/src/pages/index.tsx
@@ -1,335 +1,21 @@
-
-import React, { useEffect, useState, useRef } from 'react';
-import type { ReactElement } from 'react';
+import React, { ReactElement } from 'react';
import Head from 'next/head';
-import Link from 'next/link';
-import {
- mdiPlay,
- mdiPause,
- mdiVolumeHigh,
- mdiTuneVariant,
- mdiClockOutline,
- mdiCoffeeOutline,
- mdiBookOpenVariant,
- mdiRefresh,
- mdiWeatherRainy,
- mdiWaves,
- mdiTree,
- mdiVolumeMedium
-} from '@mdi/js';
-import BaseIcon from '../components/BaseIcon';
+import BalatroGame from '../components/Balatro/BalatroGame';
import LayoutGuest from '../layouts/Guest';
import { getPageTitle } from '../config';
-export default function WhiteNoiseHome() {
- // Audio State
- const [isPlaying, setIsPlaying] = useState(false);
- const [volume, setVolume] = useState(0.5);
- const audioRef = useRef(null);
-
- const sounds = [
- { id: 'white', name: 'White Noise', url: 'https://actions.google.com/sounds/v1/ambiences/white_noise.ogg', icon: mdiTuneVariant, color: 'indigo' },
- { id: 'rain', name: 'Heavy Rain', url: 'https://actions.google.com/sounds/v1/weather/rain_heavy_loud.ogg', icon: mdiWeatherRainy, color: 'blue' },
- { id: 'ocean', name: 'Ocean Waves', url: 'https://actions.google.com/sounds/v1/water/waves_crashing_on_shore.ogg', icon: mdiWaves, color: 'cyan' },
- { id: 'forest', name: 'Night Forest', url: 'https://actions.google.com/sounds/v1/ambiences/night_forest_with_insects.ogg', icon: mdiTree, color: 'green' },
- ];
- const [currentSound, setCurrentSound] = useState(sounds[0]);
-
- // Timer State
- const [timerMinutes, setTimerMinutes] = useState(25);
- const [timerSeconds, setTimerSeconds] = useState(0);
- const [isTimerRunning, setIsTimerRunning] = useState(false);
- const [timerMode, setTimerMode] = useState<'focus' | 'break'>('focus');
-
- // Timer Logic
- useEffect(() => {
- let interval: NodeJS.Timeout;
-
- if (isTimerRunning) {
- interval = setInterval(() => {
- if (timerSeconds > 0) {
- setTimerSeconds(timerSeconds - 1);
- } else if (timerMinutes > 0) {
- setTimerMinutes(timerMinutes - 1);
- setTimerSeconds(59);
- } else {
- // Timer finished
- const nextMode = timerMode === 'focus' ? 'break' : 'focus';
- setTimerMode(nextMode);
- setTimerMinutes(nextMode === 'focus' ? 25 : 5);
- setTimerSeconds(0);
- setIsTimerRunning(false);
- // Play a notification sound could be added here
- }
- }, 1000);
- }
-
- return () => clearInterval(interval);
- }, [isTimerRunning, timerMinutes, timerSeconds, timerMode]);
-
- const toggleTimer = () => setIsTimerRunning(!isTimerRunning);
- const resetTimer = () => {
- setIsTimerRunning(false);
- setTimerMinutes(timerMode === 'focus' ? 25 : 5);
- setTimerSeconds(0);
- };
-
- const togglePlay = () => {
- if (audioRef.current) {
- if (isPlaying) {
- audioRef.current.pause();
- } else {
- audioRef.current.play().catch(err => console.error("Audio play failed:", err));
- }
- setIsPlaying(!isPlaying);
- }
- };
-
- const changeSound = (sound: typeof sounds[0]) => {
- setCurrentSound(sound);
- setIsPlaying(false);
- if (audioRef.current) {
- audioRef.current.load();
- }
- };
-
- const handleVolumeChange = (e: React.ChangeEvent) => {
- const newVolume = parseFloat(e.target.value);
- setVolume(newVolume);
- if (audioRef.current) {
- audioRef.current.volume = newVolume;
- }
- };
-
+export default function Home() {
return (
-
+ <>
-
{getPageTitle('ZenNoise - Deep Focus Study Tools')}
-
+
{getPageTitle('Balatro - Poker Roguelike')}
+
-
-
setIsPlaying(true)}
- onPause={() => setIsPlaying(false)}
- />
-
- {/* Header */}
-
-
-
-
- Sign In
-
-
- Dashboard
-
-
-
-
-
-
- {/* Hero & Primary Player */}
-
-
-
-
-
- Design Your
- Atmosphere.
-
-
- Mixing high-fidelity noise loops with a precision study timer to help you reach a state of absolute focus.
-
-
-
-
-
-
-
-
- Now Playing: {currentSound.name}
-
-
-
-
-
-
-
- {/* Study Timer Section */}
-
-
-
-
-
-
-
Study Timer
-
Precision Pomodoro to keep your brain sharp.
-
-
-
-
- {String(timerMinutes).padStart(2, '0')}:{String(timerSeconds).padStart(2, '0')}
-
-
-
-
- {isTimerRunning ? 'Pause' : 'Start Focus'}
-
-
-
-
-
-
-
- { setTimerMode('focus'); setTimerMinutes(25); setTimerSeconds(0); setIsTimerRunning(false); }}
- className={`px-6 py-2 rounded-xl text-sm font-bold transition-all ${timerMode === 'focus' ? 'bg-white text-slate-950 shadow-lg' : 'text-slate-500 hover:text-slate-300'}`}
- >
- Focus (25m)
-
- { setTimerMode('break'); setTimerMinutes(5); setTimerSeconds(0); setIsTimerRunning(false); }}
- className={`px-6 py-2 rounded-xl text-sm font-bold transition-all ${timerMode === 'break' ? 'bg-white text-slate-950 shadow-lg' : 'text-slate-500 hover:text-slate-300'}`}
- >
- Break (5m)
-
-
-
-
-
- {/* Library Section */}
-
-
-
-
-
-
-
Sound Library
-
Switch between different high-fidelity environments.
-
-
-
- {sounds.map((sound) => (
-
changeSound(sound)}
- className={`p-6 rounded-3xl border transition-all text-left flex flex-col space-y-4 group/card ${
- currentSound.id === sound.id
- ? 'bg-white border-white text-slate-950'
- : 'bg-white/5 border-white/10 text-white hover:bg-white/10 hover:border-white/20'
- }`}
- >
-
-
-
-
-
{sound.name}
-
- {sound.id === 'white' ? 'Continuous' : 'Atmospheric'}
-
-
-
- ))}
-
-
- {/* Volume Control Overlay */}
-
-
- 0.5 ? mdiVolumeHigh : volume > 0 ? mdiVolumeMedium : mdiVolumeMedium} size={24} className="text-indigo-400" />
-
- {Math.round(volume * 100)}%
-
-
-
-
-
-
- {/* Benefits Section */}
-
-
-
-
-
-
Mask Distractions
-
Cancel out annoying background noise like construction or chatter with ease.
-
-
-
-
-
-
Improve Focus
-
Standardize your study environment to trigger "flow state" faster and more reliably.
-
-
-
-
-
-
Reduce Stress
-
Gentle noise frequencies are proven to lower cortisol levels during high-stress tasks.
-
-
-
-
- {/* Footer */}
-
-
+
+ >
);
}
-WhiteNoiseHome.getLayout = function getLayout(page: ReactElement) {
+Home.getLayout = function getLayout(page: ReactElement) {
return {page} ;
-};
+};
\ No newline at end of file