Skip to content

Commit

Permalink
Merge pull request #30 from jordan-dalby/21-subpath-fix
Browse files Browse the repository at this point in the history
Simplified server
  • Loading branch information
jordan-dalby authored Nov 5, 2024
2 parents e248567 + 639cbfd commit dbf2fbc
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 46 deletions.
3 changes: 2 additions & 1 deletion client/src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ interface LoginResponse {
token: string;
}

export const AUTH_API_URL = '/api/auth';
export const basePath = (window as any).__BASE_PATH__ || '';
export const AUTH_API_URL = `${basePath}/api/auth`;

interface ApiError extends Error {
status?: number;
Expand Down
3 changes: 2 additions & 1 deletion client/src/api/snippets.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Snippet } from '../types/types';

export const API_URL = '/api/snippets';
export const basePath = (window as any).__BASE_PATH__ || '';
export const API_URL = `${basePath}/api/snippets`;

interface ApiError extends Error {
status?: number;
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
ports:
- "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
Expand Down
112 changes: 68 additions & 44 deletions server/src/app.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,80 @@
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const fs = require('fs');
const { initializeDatabase } = require('./config/database');
const snippetRoutes = require('./routes/snippetRoutes');
const authRoutes = require('./routes/authRoutes');
const { authenticateToken } = require('./middleware/auth');
const { join } = require('path');
const fs = require('fs');

const expressApp = express();
const port = 5000;
const app = express();
const PORT = 5000;

function app(server) {
server.use(bodyParser.json());
server.set('trust proxy', true);
app.use(express.json());

const clientBuildPath = '/client/build';
const basePath = process.env.BASE_PATH || '';
const buildPath = join(__dirname, '../../client/build');
const assetsPath = join(buildPath, 'assets');

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 {
res.sendFile(path.join(clientBuildPath, 'index.html'));
}
});
}

async function startServer() {
try {
await initializeDatabase();
return new Promise((resolve) => {
expressApp.listen(port, () => {
console.log(`Server running on port ${port}`);
resolve();
});
});
} catch (error) {
console.error('Failed to initialize database:', error);
throw error;
app.use(`${basePath}/api/auth`, authRoutes);
app.use(`${basePath}/api/snippets`, authenticateToken, snippetRoutes);

app.use(`${basePath}/assets`, express.static(assetsPath));
app.use(`${basePath}/monacoeditorwork`, express.static(join(buildPath, 'monacoeditorwork')));

app.use(basePath, express.static(buildPath, { index: false }));

/*
* A bit of a hack, we need to manually rewrite the HTML to support base paths with ingress
* If given a base path of /bytestash, the index.html file will still be using /assets/xyz.css
* But of course the files are not there on ingress, so we need to change them to /bytestash/assets/xyz.css
* It's a bit of a hacky mess but this is the only solution I figured out without directly modifying vite.config.ts
* on the client, but this will affect everyone, so not a viable solution
*
* We're also injecting the base path into the HTML so that the client can know the value without relying on an
* environment variable, which would be baked into the client code at build time, which is not acceptable in this case
*/
app.get(`${basePath}/*`, (req, res, next) => {
if (req.url.startsWith(`${basePath}/api`)) {
return next();
}

// Don't cache, if the base path changes the previous index.html file is still used which will have incorrect paths
res.set({
'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
});

fs.readFile(join(buildPath, 'index.html'), 'utf8', (err, data) => {
if (err) {
return res.status(500).send('Error loading index.html');
}
}

const modifiedHtml = data.replace(
/(src|href)="\/assets\//g,
`$1="${basePath}/assets/`
).replace(
/\/monacoeditorwork\//g,
`${basePath}/monacoeditorwork/`
);

if (require.main === module) {
app(expressApp);
startServer().catch(err => {
console.error('Failed to start server:', err);
process.exit(1);
});
}
const scriptInjection = `<script>window.__BASE_PATH__ = "${basePath}";</script>`;
const injectedHtml = modifiedHtml.replace(
'</head>',
`${scriptInjection}</head>`
);

res.send(injectedHtml);
});
});

(async () => {
await initializeDatabase();

module.exports = { app, startServer };
return new Promise((resolve) => {
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
resolve();
});
});
})();

0 comments on commit dbf2fbc

Please sign in to comment.