diff --git a/apps/client/index.html b/apps/client/index.html
index c46ec4f7f..7053111d3 100644
--- a/apps/client/index.html
+++ b/apps/client/index.html
@@ -3,14 +3,14 @@
-
+
-
-
-
-
+
+
+
+
ontime
diff --git a/apps/client/public/manifest.json b/apps/client/public/manifest.json
index 3c4b0e1a8..44d458dd5 100644
--- a/apps/client/public/manifest.json
+++ b/apps/client/public/manifest.json
@@ -11,8 +11,8 @@
"type": "image/png"
}
],
- "scope": "/",
- "start_url": "/",
+ "scope": "./",
+ "start_url": "./",
"display": "",
"theme_color": "#121212",
"background_color": "#ffffff"
diff --git a/apps/client/public/site.webmanifest b/apps/client/public/site.webmanifest
index 5e9522c3d..88ea7c3b1 100644
--- a/apps/client/public/site.webmanifest
+++ b/apps/client/public/site.webmanifest
@@ -1,7 +1,7 @@
{
"name": "",
"short_name": "",
- "icons": [{ "src": "/ontime-logo.png", "sizes": "295x295", "type": "image/png" }],
+ "icons": [{ "src": "ontime-logo.png", "sizes": "295x295", "type": "image/png" }],
"theme_color": "#2B5ABC",
"background_color": "#101010",
"display": "standalone"
diff --git a/apps/server/src/middleware/__tests__/authenticate.test.ts b/apps/server/src/middleware/__tests__/authenticate.test.ts
new file mode 100644
index 000000000..aa1bd897f
--- /dev/null
+++ b/apps/server/src/middleware/__tests__/authenticate.test.ts
@@ -0,0 +1,20 @@
+import { describe, expect, it } from 'vitest';
+
+import { isPublicAssetRequest } from '../authenticate.js';
+
+describe('isPublicAssetRequest()', () => {
+ it('allows root public assets without a prefix', () => {
+ expect(isPublicAssetRequest('/site.webmanifest', '')).toBe(true);
+ expect(isPublicAssetRequest('/manifest.json', '')).toBe(true);
+ });
+
+ it('allows prefixed public assets in cloud deployments', () => {
+ expect(isPublicAssetRequest('/stage-hash/site.webmanifest', '/stage-hash')).toBe(true);
+ expect(isPublicAssetRequest('/stage-hash/ontime-logo.png?cache=1', '/stage-hash')).toBe(true);
+ });
+
+ it('keeps non-public paths protected', () => {
+ expect(isPublicAssetRequest('/stage-hash/data', '/stage-hash')).toBe(false);
+ expect(isPublicAssetRequest('/backstage', '')).toBe(false);
+ });
+});
diff --git a/apps/server/src/middleware/authenticate.ts b/apps/server/src/middleware/authenticate.ts
index ed6da3cc9..74b6f9ddf 100644
--- a/apps/server/src/middleware/authenticate.ts
+++ b/apps/server/src/middleware/authenticate.ts
@@ -21,6 +21,20 @@ const publicAssets = new Set([
'/site.webmanifest',
]);
+export function isPublicAssetRequest(originalUrl: string, prefix: string): boolean {
+ const pathname = originalUrl.split('?')[0];
+
+ if (publicAssets.has(pathname)) {
+ return true;
+ }
+
+ if (prefix && pathname.startsWith(prefix)) {
+ return publicAssets.has(pathname.slice(prefix.length) || '/');
+ }
+
+ return false;
+}
+
/**
* Creates a login router with the provided prefix
* @param {string} prefix - Prefix is used for the client hashes in Ontime Cloud
@@ -81,7 +95,7 @@ export function makeAuthenticateMiddleware(prefix: string) {
function authenticateAndRedirect(req: Request, res: Response, next: NextFunction) {
// Allow access to specific public assets without authentication
- if (publicAssets.has(req.originalUrl)) {
+ if (isPublicAssetRequest(req.originalUrl, prefix)) {
return next();
}