diff --git a/client/src/api/auth.ts b/client/src/api/auth.ts index a62d630..f78f7ce 100644 --- a/client/src/api/auth.ts +++ b/client/src/api/auth.ts @@ -1,108 +1,97 @@ interface AuthConfig { - authRequired: boolean; - } - - interface LoginResponse { - token: string; - } - - declare global { - interface Window { - BASE_PATH?: string; - } - } - - const getBasePath = (): string => { - return window?.BASE_PATH?.endsWith('/') ? window.BASE_PATH.slice(0, -1) : window.BASE_PATH || ''; - }; - - const BASE_PATH = getBasePath(); - export const AUTH_API_URL = `${BASE_PATH}/api/auth`; - - interface ApiError extends Error { - status?: number; - } - - export const AUTH_ERROR_EVENT = 'bytestash:auth_error'; - export const authErrorEvent = new CustomEvent(AUTH_ERROR_EVENT); - - const handleResponse = async (response: Response) => { - if (response.status === 401 || response.status === 403) { + authRequired: boolean; +} + +interface LoginResponse { + token: string; +} + +export const AUTH_API_URL = '/api/auth'; + +interface ApiError extends Error { + status?: number; +} + +export const AUTH_ERROR_EVENT = 'bytestash:auth_error'; +export const authErrorEvent = new CustomEvent(AUTH_ERROR_EVENT); + +const handleResponse = async (response: Response) => { + if (response.status === 401 || response.status === 403) { window.dispatchEvent(authErrorEvent); const error = new Error('Authentication required') as ApiError; error.status = response.status; throw error; - } - - if (!response.ok) { + } + + if (!response.ok) { const text = await response.text(); console.error('Error response body:', text); const error = new Error(`Request failed: ${response.status} ${response.statusText}`) as ApiError; error.status = response.status; throw error; - } - - return response; + } + + return response; +}; + +const getHeaders = (includeAuth = false) => { + const headers: Record = { + 'Content-Type': 'application/json', }; - const getHeaders = (includeAuth = false) => { - const headers: Record = { - 'Content-Type': 'application/json', - }; - - if (includeAuth) { + if (includeAuth) { const token = localStorage.getItem('token'); if (token) { - headers['Authorization'] = `Bearer ${token}`; + headers['Authorization'] = `Bearer ${token}`; } - } - - return headers; - }; + } - export const getAuthConfig = async (): Promise => { - try { + return headers; +}; + +export const getAuthConfig = async (): Promise => { + try { const response = await fetch(`${AUTH_API_URL}/config`); await handleResponse(response); return response.json(); - } catch (error) { + } catch (error) { console.error('Error fetching auth config:', error); throw error; - } - }; - - export const verifyToken = async (): Promise => { - try { + } +}; + +export const verifyToken = async (): Promise => { + try { const response = await fetch(`${AUTH_API_URL}/verify`, { - headers: getHeaders(true) + headers: getHeaders(true) }); if (response.status === 401 || response.status === 403) { - return false; + return false; } const data = await response.json(); return data.valid; - } catch (error) { + } catch (error) { console.error('Error verifying token:', error); return false; - } - }; - - export const login = async (username: string, password: string): Promise => { - try { + } +}; + +export const login = async (username: string, password: string): Promise => { + try { const response = await fetch(`${AUTH_API_URL}/login`, { - method: 'POST', - headers: getHeaders(), - body: JSON.stringify({ username, password }) + method: 'POST', + headers: getHeaders(), + body: JSON.stringify({ username, password }) }); - + await handleResponse(response); const data: LoginResponse = await response.json(); return data.token; - } catch (error) { + } catch (error) { console.error('Error logging in:', error); throw error; - } - }; \ No newline at end of file + } +}; \ No newline at end of file diff --git a/client/src/api/snippets.ts b/client/src/api/snippets.ts index 3612720..4413bdf 100644 --- a/client/src/api/snippets.ts +++ b/client/src/api/snippets.ts @@ -1,124 +1,113 @@ import { Snippet } from '../types/types'; -declare global { - interface Window { - BASE_PATH?: string; - } -} - -const getBasePath = (): string => { - return window?.BASE_PATH?.endsWith('/') ? window.BASE_PATH.slice(0, -1) : window.BASE_PATH || ''; -}; - -const BASE_PATH = getBasePath(); -export const API_URL = `${BASE_PATH}/api/snippets`; +export const API_URL = '/api/snippets'; interface ApiError extends Error { - status?: number; + status?: number; } export const AUTH_ERROR_EVENT = 'bytestash:auth_error'; export const authErrorEvent = new CustomEvent(AUTH_ERROR_EVENT); const handleResponse = async (response: Response) => { - if (response.status === 401 || response.status === 403) { - window.dispatchEvent(authErrorEvent); - - const error = new Error('Authentication required') as ApiError; - error.status = response.status; - throw error; - } + if (response.status === 401 || response.status === 403) { + window.dispatchEvent(authErrorEvent); + + const error = new Error('Authentication required') as ApiError; + error.status = response.status; + throw error; + } - if (!response.ok) { - const text = await response.text(); - console.error('Error response body:', text); - const error = new Error(`Request failed: ${response.status} ${response.statusText}`) as ApiError; - error.status = response.status; - throw error; - } + if (!response.ok) { + const text = await response.text(); + console.error('Error response body:', text); + const error = new Error(`Request failed: ${response.status} ${response.statusText}`) as ApiError; + error.status = response.status; + throw error; + } - return response; + return response; }; const getHeaders = () => { - const headers: Record = { - 'Content-Type': 'application/json', - }; - - const token = localStorage.getItem('token'); - if (token) { - headers['Authorization'] = `Bearer ${token}`; - } - - return headers; + const headers: Record = { + 'Content-Type': 'application/json', + }; + + const token = localStorage.getItem('token'); + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + return headers; }; export const fetchSnippets = async (): Promise => { - try { - const response = await fetch(API_URL, { - headers: getHeaders() - }); - - await handleResponse(response); - const text = await response.text(); - try { - return JSON.parse(text); - } catch (e) { - console.error('Failed to parse JSON:', e); - console.error('Full response:', text); - throw e; + const response = await fetch(API_URL, { + headers: getHeaders() + }); + + await handleResponse(response); + const text = await response.text(); + + try { + return JSON.parse(text); + } catch (e) { + console.error('Failed to parse JSON:', e); + console.error('Full response:', text); + throw e; + } + } catch (error) { + console.error('Error fetching snippets:', error); + throw error; } - } catch (error) { - console.error('Error fetching snippets:', error); - throw error; - } }; export const createSnippet = async (snippet: Omit): Promise => { - try { - const response = await fetch(API_URL, { - method: 'POST', - headers: getHeaders(), - body: JSON.stringify(snippet), - }); - - await handleResponse(response); - return response.json(); - } catch (error) { - console.error('Error creating snippet:', error); - throw error; - } + try { + const response = await fetch(API_URL, { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(snippet), + }); + + await handleResponse(response); + return response.json(); + } catch (error) { + console.error('Error creating snippet:', error); + throw error; + } }; export const deleteSnippet = async (id: string): Promise => { - try { - const response = await fetch(`${API_URL}/${id}`, { - method: 'DELETE', - headers: getHeaders(), - }); - - await handleResponse(response); - await response.json(); - return id; - } catch (error) { - console.error('Error deleting snippet:', error); - throw error; - } + try { + const response = await fetch(`${API_URL}/${id}`, { + method: 'DELETE', + headers: getHeaders(), + }); + + await handleResponse(response); + await response.json(); + return id; + } catch (error) { + console.error('Error deleting snippet:', error); + throw error; + } }; export const editSnippet = async (id: string, snippet: Omit): Promise => { - try { - const response = await fetch(`${API_URL}/${id}`, { - method: 'PUT', - headers: getHeaders(), - body: JSON.stringify(snippet), - }); - - await handleResponse(response); - return response.json(); - } catch (error) { - console.error('Error updating snippet:', error); - throw error; - } + try { + const response = await fetch(`${API_URL}/${id}`, { + method: 'PUT', + headers: getHeaders(), + body: JSON.stringify(snippet), + }); + + await handleResponse(response); + return response.json(); + } catch (error) { + console.error('Error updating snippet:', error); + throw error; + } }; \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 5ee0daf..f373a0c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -4,9 +4,8 @@ services: context: . dockerfile: Dockerfile ports: - - "5000:5000" + - "5001:5000" environment: - - BASE_PATH= # if auth username or password are left blank then authorisation is disabled # the username used for logging in - AUTH_USERNAME=bytestash diff --git a/server/src/app.js b/server/src/app.js index 1d1e7d6..a7d4fae 100644 --- a/server/src/app.js +++ b/server/src/app.js @@ -11,53 +11,21 @@ const expressApp = express(); const port = 5000; function app(server) { - const basePath = process.env.BASE_PATH; server.use(bodyParser.json()); server.set('trust proxy', true); const clientBuildPath = '/client/build'; - const injectBasePath = (req, res) => { - const indexPath = path.join(clientBuildPath, 'index.html'); - let html = fs.readFileSync(indexPath, 'utf8'); - - const script = ``; - html = html.replace('', '' + script); - - res.send(html); - }; - - if (basePath) { - const normalizedBasePath = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath; - - server.use(normalizedBasePath, express.static(clientBuildPath)); - server.use(express.static(clientBuildPath, { - index: false - })); - - server.use(`${normalizedBasePath}/api/auth`, authRoutes); - server.use(`${normalizedBasePath}/api/snippets`, authenticateToken, snippetRoutes); - server.use('/api/auth', authRoutes); - server.use('/api/snippets', authenticateToken, snippetRoutes); - - server.get(normalizedBasePath, injectBasePath); - server.get(`${normalizedBasePath}/*`, injectBasePath); - server.get('/', (req, res) => { - res.redirect(normalizedBasePath); - }); - } else { - server.use(express.static(clientBuildPath)); - server.use('/api/auth', authRoutes); - server.use('/api/snippets', authenticateToken, snippetRoutes); - server.get('*', injectBasePath); - } - - server.use((req, res, next) => { + server.use(express.static(clientBuildPath)); + server.use('/api/auth', authRoutes); + server.use('/api/snippets', authenticateToken, snippetRoutes); + + server.get('*', (req, res) => { if (req.path.includes('/assets/') || req.path.endsWith('.json')) { console.log('404 for static file:', req.path); res.status(404).send('File not found'); } else { - next(); + res.sendFile(path.join(clientBuildPath, 'index.html')); } }); } @@ -67,7 +35,7 @@ async function startServer() { await initializeDatabase(); return new Promise((resolve) => { expressApp.listen(port, () => { - console.log(`Server running on port ${port}${process.env.BASE_PATH || ''}`); + console.log(`Server running on port ${port}`); resolve(); }); });