Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions backend/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,6 @@ services:
condition: service_started
mongo:
condition: service_healthy
nginx:
image: nginx:alpine
container_name: ctf-nginx
restart: unless-stopped
ports:
- "3000:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- ctf-network
depends_on:
- app

networks:
ctf-network:
driver: bridge
Expand Down
32 changes: 0 additions & 32 deletions backend/nginx.conf

This file was deleted.

21 changes: 12 additions & 9 deletions backend/src/utils/dockerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ import path from 'node:path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const dockerConfig: Dockerode.DockerOptions =
configEnv.nodeEnv === 'production'
? {
host: 'EC2:IP_Address', //fron env it should not be hardcoded
protocol: 'https',
port: 2376,
ca: fs.readFileSync(path.join(__dirname, 'ca.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
}
: {};

// for ec2 connection
const docker = new Dockerode({
host: 'EC2:IP_Address', //fron env it should not be hardcoded
protocol: 'https',
port: 2376,
ca: fs.readFileSync(path.join(__dirname, 'ca.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
});
const docker = new Dockerode(dockerConfig);
/**
* Generates a random port number in the range [3001, 4000) that is not in use or reserved.
*
Expand Down
6 changes: 5 additions & 1 deletion frontend/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
upstream backendserver {
server ctf-backend:3000;
}

server {
listen 0.0.0.0:80;
server_name _;
Expand Down Expand Up @@ -44,7 +48,7 @@ server {
}
# API proxy to backend
location /api/ {
proxy_pass http://127.0.0.1:3000/api/;
proxy_pass http://backendserver;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { useAuth } from '../../hooks/useAuth';
import { useNavigate, Link } from 'react-router-dom';
import { getErrorMessage } from '../../utils/errorHandler';
Expand All @@ -16,7 +16,11 @@ const Login: React.FC = () => {
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);

if (isAuthenticated) navigate(ROUTES.CHALLENGES);
useEffect(() => {
if (isAuthenticated) {
navigate(ROUTES.CHALLENGES);
}
}, [isAuthenticated, navigate]);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/challenges/ChallengeDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ const ChallengeDetail: React.FC = () => {
setMessage({ type: 'error', text: 'Incorrect flag. Try again!' });
}
} catch (error: unknown) {
getErrorMessage(error);
const errorMessage = getErrorMessage(error);
setMessage({ type: 'error', text: errorMessage });
} finally {
setLoading(false);
}
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/common/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from 'react';
import { useAuth } from '../../hooks/useAuth';
import { Navigate } from 'react-router-dom';
import { ROUTES } from '../../utils/constants';

const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const { isAuthenticated } = useAuth();
return isAuthenticated ? <>{children}</> : <Navigate to="/login" replace />;
return isAuthenticated ? (
<>{children}</>
) : (
<Navigate to={ROUTES.LOGIN} replace />
);
};
export default ProtectedRoute;
6 changes: 4 additions & 2 deletions frontend/src/components/layout/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { ROUTES } from '../../utils/constants';
import './Footer.css';

const Footer: React.FC = () => {
Expand All @@ -13,10 +15,10 @@ const Footer: React.FC = () => {
<h4>Quick Links</h4>
<ul>
<li>
<a href="/challenges">Challenges</a>
<Link to={ROUTES.CHALLENGES}>Challenges</Link>
</li>
<li>
<a href="/leaderboard">Leaderboard</a>
<Link to={ROUTES.LEADERBOARD}>Leaderboard</Link>
</li>
<li>
<a href="/about">About</a>
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/layout/Navbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@

.btn-login,
.btn-register {
background: white;
color: var(--primary);
border-color: white;
}
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/components/layout/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link, NavLink, useNavigate } from 'react-router-dom';
import { useAuth } from '../../hooks/useAuth';
import './Navbar.css';
import { useToast } from '../../hooks/useToast';
import { ROUTES } from '../../utils/constants';

const Navbar: React.FC = () => {
const { isAuthenticated, logout } = useAuth();
Expand All @@ -12,21 +13,21 @@ const Navbar: React.FC = () => {
const handleLogout = () => {
logout();
showToast('Logged out successfully', 'success');
navigate('/login');
navigate(ROUTES.LOGIN);
};

return (
<nav className="navbar">
<div className="navbar-container">
<Link to="/" className="navbar-logo">
<Link to={ROUTES.HOME} className="navbar-logo">
Ctf Platform
</Link>

<ul className="navbar-menu">
{isAuthenticated ? (
<>
<li>
<NavLink to="/challenges">Challenges</NavLink>
<NavLink to={ROUTES.CHALLENGES}>Challenges</NavLink>
</li>
<li>
<button onClick={handleLogout} className="btn-logout">
Expand All @@ -37,12 +38,12 @@ const Navbar: React.FC = () => {
) : (
<>
<li>
<Link to="/login" className="btn-login">
<Link to={ROUTES.LOGIN} className="btn-login">
Login
</Link>
</li>
<li>
<Link to="/register" className="btn-register">
<Link to={ROUTES.REGISTER} className="btn-register">
Register
</Link>
</li>
Expand Down
19 changes: 10 additions & 9 deletions frontend/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { ROUTES } from '../../utils/constants';
import './Sidebar.css';

const Sidebar: React.FC = () => {
const location = useLocation();

const categories = [
{ name: 'All', path: '/challenges' },
{ name: 'Web', path: '/challenges?category=web' },
{ name: 'Crypto', path: '/challenges?category=crypto' },
{ name: 'Reverse', path: '/challenges?category=reverse' },
{ name: 'Forensics', path: '/challenges?category=forensics' },
{ name: 'Pwn', icon: '', path: '/challenges?category=pwn' },
{ name: 'All', path: ROUTES.CHALLENGES },
{ name: 'Web', path: `${ROUTES.CHALLENGES}?category=web` },
{ name: 'Crypto', path: `${ROUTES.CHALLENGES}?category=crypto` },
{ name: 'Reverse', path: `${ROUTES.CHALLENGES}?category=reverse` },
{ name: 'Forensics', path: `${ROUTES.CHALLENGES}?category=forensics` },
{ name: 'Pwn', icon: '', path: `${ROUTES.CHALLENGES}?category=pwn` },
];

return (
Expand Down Expand Up @@ -41,13 +42,13 @@ const Sidebar: React.FC = () => {
<h3>Difficulty</h3>
<ul className="sidebar-list">
<li>
<Link to="/challenges?difficulty=easy">🟢 Easy</Link>
<Link to={`${ROUTES.CHALLENGES}?difficulty=easy`}>🟢 Easy</Link>
</li>
<li>
<Link to="/challenges?difficulty=medium">🟡 Medium</Link>
<Link to={`${ROUTES.CHALLENGES}?difficulty=medium`}>🟡 Medium</Link>
</li>
<li>
<Link to="/challenges?difficulty=hard">🔴 Hard</Link>
<Link to={`${ROUTES.CHALLENGES}?difficulty=hard`}>🔴 Hard</Link>
</li>
</ul>
</div>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
import { ROUTES } from '../utils/constants';
import './Home.css';

const Home: React.FC = () => {
Expand All @@ -15,14 +16,14 @@ const Home: React.FC = () => {
</p>
{!isAuthenticated && (
<div className="hero-actions">
<Link to="/register" className="btn btn-primary btn-large">
<Link to={ROUTES.REGISTER} className="btn btn-primary btn-large">
Get Started
</Link>
</div>
)}
{isAuthenticated && (
<div className="hero-actions">
<Link to="/challenges" className="btn btn-primary btn-large">
<Link to={ROUTES.CHALLENGES} className="btn btn-primary btn-large">
View Challenges
</Link>
</div>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/pages/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// src/pages/Profile.tsx
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
import {
submissionService,
type Submission,
} from '../services/submissionService';
import { ROUTES } from '../utils/constants';
import './Profile.css';

const Profile: React.FC = () => {
Expand Down Expand Up @@ -51,9 +53,9 @@ const Profile: React.FC = () => {
<h1 className="text-2xl mb-4">Profile</h1>
<p>
No user data found.{' '}
<a href="/login" className="text-blue-400 hover:underline">
<Link to={ROUTES.LOGIN} className="text-blue-400 hover:underline">
Login here
</a>
</Link>
</p>
</div>
</div>
Expand Down
19 changes: 12 additions & 7 deletions frontend/src/services/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import axios from 'axios';
import { API_BASE_URL } from '../utils/constants';
import {
API_BASE_URL,
ROUTES,
LOCAL_STORAGE_KEYS,
AUTH_PREFIX,
} from '../utils/constants';

const api = axios.create({
baseURL: API_BASE_URL,
Expand All @@ -9,10 +14,10 @@ const api = axios.create({

api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
const token = localStorage.getItem(LOCAL_STORAGE_KEYS.TOKEN);
if (token) {
config.headers = config.headers ?? {};
config.headers.Authorization = `Bearer ${token}`;
config.headers.Authorization = `${AUTH_PREFIX}${token}`;
}
return config;
},
Expand All @@ -25,15 +30,15 @@ api.interceptors.response.use(
const status = error.response?.status;

// Only redirect if token exists AND backend explicitly rejects it
if (status === 401 && localStorage.getItem('token')) {
if (status === 401 && localStorage.getItem(LOCAL_STORAGE_KEYS.TOKEN)) {
console.warn('Unauthorized – token invalid or expired');

// Clear auth ONCE
localStorage.removeItem('token');
localStorage.removeItem('user');
localStorage.removeItem(LOCAL_STORAGE_KEYS.TOKEN);
localStorage.removeItem(LOCAL_STORAGE_KEYS.USER);

// Hard redirect to reset state
window.location.replace('/login');
window.location.replace(ROUTES.LOGIN);
}

return Promise.reject(error);
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/services/authService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios, { type AxiosResponse } from 'axios';
import { API_BASE_URL, ROUTES } from '../utils/constants';
import { API_BASE_URL, LOCAL_STORAGE_KEYS, ROUTES } from '../utils/constants';

export interface RegisterData {
email: string;
Expand Down Expand Up @@ -47,23 +47,23 @@ export const authService = {

const user = response.data.userWithoutPassword;

localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem(LOCAL_STORAGE_KEYS.USER, JSON.stringify(user));

return user;
},

async logout(): Promise<void> {
await api.post('/logout');
localStorage.removeItem('user');
await api.post(ROUTES.LOGOUT);
localStorage.removeItem(LOCAL_STORAGE_KEYS.USER);
},

getCurrentUser(): User | null {
const raw = localStorage.getItem('user');
const raw = localStorage.getItem(LOCAL_STORAGE_KEYS.USER);
return raw ? JSON.parse(raw) : null;
},

updateUser(user: User): void {
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem(LOCAL_STORAGE_KEYS.USER, JSON.stringify(user));
},
};

Expand Down
Loading