Skip to content

Commit 235fa63

Browse files
Copilotsstidlqodo-free-for-open-source-projects[bot]
authored
Add feature switch for new design via config file, URL parameters, and Docker (#742)
* Initial plan * Add feature switch for new design with config and URL parameter support Co-authored-by: sstidl <[email protected]> * Improve error handling and prevent infinite redirect loops Co-authored-by: sstidl <[email protected]> * Update Dockerfiles and entrypoint to support design feature switch Co-authored-by: sstidl <[email protected]> * Update design-switch.js Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com> * fix: copy actions in entrypoint * Restructure design switch to place both designs at root level Co-authored-by: sstidl <[email protected]> * Flatten frontend assets in Docker to eliminate frontend directory Co-authored-by: sstidl <[email protected]> * fix: entrypoint settings & server-list disable entrypoint bash debug * add link to modern design --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: sstidl <[email protected]> Co-authored-by: sstidl <[email protected]> Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
1 parent 3a0e6b3 commit 235fa63

File tree

10 files changed

+891
-513
lines changed

10 files changed

+891
-513
lines changed

DESIGN_SWITCH.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Design Feature Switch
2+
3+
LibreSpeed now supports switching between the classic design and the new modern design.
4+
5+
## Default Behavior
6+
7+
By default, LibreSpeed uses the **classic design** (located in `index-classic.html`).
8+
9+
## Architecture
10+
11+
### File Structure (Non-Docker)
12+
- **`index.html`** - Entry point (lightweight switcher)
13+
- **`index-classic.html`** - Classic design at root
14+
- **`index-modern.html`** - Modern design at root (references assets in subdirectories)
15+
- **`frontend/`** - Directory containing modern design assets (CSS, JS, images, fonts) - kept for non-Docker deployments
16+
17+
### File Structure (Docker)
18+
In Docker deployments, the frontend assets are flattened to root-level subdirectories:
19+
- **`index.html`** - Entry point (lightweight switcher)
20+
- **`index-classic.html`** - Classic design
21+
- **`index-modern.html`** - Modern design
22+
- **`styling/`** - CSS files for modern design
23+
- **`javascript/`** - JS files for modern design
24+
- **`images/`** - Images for modern design
25+
- **`fonts/`** - Fonts for modern design
26+
- **No `frontend/` directory** - Assets are copied directly to root subdirectories
27+
28+
### Benefits of Root-Level Design Files
29+
✅ Both designs at same level - no path confusion
30+
`results/` accessible from both designs with same relative path
31+
`backend/` accessible from both designs with same relative path
32+
✅ No subdirectory nesting issues
33+
✅ Clean separation of concerns
34+
✅ Docker containers have no `frontend/` parent directory
35+
36+
## Browser Compatibility
37+
38+
The feature switch uses modern JavaScript features (URLSearchParams, fetch API). It is compatible with all modern browsers. The new design itself requires modern browser features and has no backwards compatibility with older browsers (see `frontend/README.md`).
39+
40+
## Enabling the New Design
41+
42+
There are two ways to enable the new design:
43+
44+
### Method 1: Configuration File (Persistent)
45+
46+
Edit the `config.json` file in the root directory and set `useNewDesign` to `true`:
47+
48+
```json
49+
{
50+
"useNewDesign": true
51+
}
52+
```
53+
54+
This will make the new design the default for all users visiting your site.
55+
56+
### Method 2: URL Parameter (Temporary Override)
57+
58+
You can override the configuration by adding a URL parameter:
59+
60+
- To use the new design: `http://yoursite.com/?design=new`
61+
- To use the old design: `http://yoursite.com/?design=old`
62+
63+
URL parameters take precedence over the configuration file, making them useful for testing or allowing users to choose their preferred design.
64+
65+
## Design Locations
66+
67+
### Non-Docker Deployments
68+
- **Entry Point**: Root `index.html` file (lightweight redirect page)
69+
- **Old Design**: `index-classic.html` at root
70+
- **New Design**: `index-modern.html` at root (references assets in `frontend/` subdirectory)
71+
- **Assets**: Frontend assets (CSS, JS, images, fonts) in `frontend/` subdirectory
72+
73+
### Docker Deployments
74+
- **Entry Point**: Root `index.html` file (lightweight redirect page)
75+
- **Old Design**: `index-classic.html` at root
76+
- **New Design**: `index-modern.html` at root (references assets in root subdirectories)
77+
- **Assets**: Frontend assets copied directly to root subdirectories (`styling/`, `javascript/`, `images/`, `fonts/`)
78+
- **No `frontend/` directory** - Assets are flattened to root level
79+
80+
Both designs are at the same directory level, ensuring that relative paths to shared resources like `backend/` and `results/` work correctly for both.
81+
82+
## Technical Details
83+
84+
The feature switch is implemented in `design-switch.js`, which is loaded by the root `index.html`. It checks:
85+
86+
1. First, URL parameters (`?design=new` or `?design=old`)
87+
2. Then, the `config.json` configuration file
88+
3. Redirects to either `index-classic.html` or `index-modern.html`
89+
90+
Both design HTML files are at the root level, eliminating path issues.
91+
92+
### Non-Docker
93+
The modern design references assets from the `frontend/` subdirectory (e.g., `frontend/styling/index.css`), while both designs can access shared resources like `backend/` and `results/` using the same relative paths.
94+
95+
### Docker
96+
In Docker deployments, the `frontend/` directory is flattened during container startup. Assets are copied directly to root-level subdirectories (`styling/`, `javascript/`, `images/`, `fonts/`), and `index-modern.html` references these root-level paths. This eliminates the `frontend/` parent directory in the container.

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ COPY results/*.php /speedtest/results/
2121
COPY results/*.ttf /speedtest/results/
2222

2323
COPY *.js /speedtest/
24+
COPY index.html /speedtest/
25+
COPY index-classic.html /speedtest/
26+
COPY index-modern.html /speedtest/
27+
COPY config.json /speedtest/
2428
COPY favicon.ico /speedtest/
2529

2630
COPY docker/servers.json /servers.json
@@ -36,6 +40,7 @@ ENV TELEMETRY=false
3640
ENV ENABLE_ID_OBFUSCATION=false
3741
ENV REDACT_IP_ADDRESSES=false
3842
ENV WEBPORT=8080
43+
ENV USE_NEW_DESIGN=false
3944

4045
# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
4146
STOPSIGNAL SIGWINCH

Dockerfile.alpine

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ COPY results/*.php /speedtest/results/
3535
COPY results/*.ttf /speedtest/results/
3636

3737
COPY *.js /speedtest/
38+
COPY index.html /speedtest/
39+
COPY index-classic.html /speedtest/
40+
COPY index-modern.html /speedtest/
41+
COPY config.json /speedtest/
3842
COPY favicon.ico /speedtest/
3943

4044
COPY docker/servers.json /servers.json
@@ -50,6 +54,7 @@ ENV TELEMETRY=false
5054
ENV ENABLE_ID_OBFUSCATION=false
5155
ENV REDACT_IP_ADDRESSES=false
5256
ENV WEBPORT=8080
57+
ENV USE_NEW_DESIGN=false
5358

5459
# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
5560
STOPSIGNAL SIGWINCH

config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"useNewDesign": false
3+
}

design-switch.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Feature switch for enabling the new LibreSpeed design
3+
*
4+
* This script checks for:
5+
* 1. URL parameter: ?design=new or ?design=old
6+
* 2. Configuration file: config.json with useNewDesign flag
7+
*
8+
* Default behavior: Shows the old design
9+
*
10+
* Note: This script is only loaded on the root index.html
11+
*/
12+
(function() {
13+
'use strict';
14+
15+
// Don't run this script if we're already on a specific design page
16+
// This prevents infinite redirect loops
17+
const currentPath = window.location.pathname;
18+
if (currentPath.includes('index-classic.html') || currentPath.includes('index-modern.html')) {
19+
return;
20+
}
21+
22+
// Check URL parameters first (they override config)
23+
const urlParams = new URLSearchParams(window.location.search);
24+
const designParam = urlParams.get('design');
25+
26+
if (designParam === 'new') {
27+
redirectToNewDesign();
28+
return;
29+
}
30+
31+
if (designParam === 'old' || designParam === 'classic') {
32+
redirectToOldDesign();
33+
return;
34+
}
35+
36+
// Check config.json for design preference
37+
try {
38+
const xhr = new XMLHttpRequest();
39+
// Use a synchronous request to prevent a flash of the old design before redirecting
40+
xhr.open('GET', 'config.json', false);
41+
xhr.send(null);
42+
43+
// Check for a successful response, but not 304 Not Modified, which can have an empty response body
44+
if (xhr.status >= 200 && xhr.status < 300) {
45+
const config = JSON.parse(xhr.responseText);
46+
if (config.useNewDesign === true) {
47+
redirectToNewDesign();
48+
} else {
49+
redirectToOldDesign();
50+
}
51+
} else {
52+
// Config not found or error - default to old design
53+
redirectToOldDesign();
54+
}
55+
} catch (error) {
56+
// If there's any error (e.g., network, JSON parse), default to old design
57+
console.log('Using default (old) design:', error.message || 'config error');
58+
redirectToOldDesign();
59+
}
60+
61+
function redirectToNewDesign() {
62+
// Preserve any URL parameters when redirecting
63+
const currentParams = window.location.search;
64+
window.location.href = 'index-modern.html' + currentParams;
65+
}
66+
67+
function redirectToOldDesign() {
68+
// Preserve any URL parameters when redirecting
69+
const currentParams = window.location.search;
70+
window.location.href = 'index-classic.html' + currentParams;
71+
}
72+
})();

doc_docker.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ The test can be accessed on port 80.
5757
Here's a list of additional environment variables available in this mode:
5858

5959
* __`TITLE`__: Title of your speed test. Default value: `LibreSpeed`
60+
* __`USE_NEW_DESIGN`__: When set to `true`, enables the new modern frontend design. When set to `false` (default), uses the classic design. The design can also be switched using URL parameters (`?design=new` or `?design=old`). Default value: `false`
6061
* __`TELEMETRY`__: Whether to enable telemetry or not. If enabled, you maybe want your data to be persisted. See below. Default value: `false`
6162
* __`ENABLE_ID_OBFUSCATION`__: When set to true with telemetry enabled, test IDs are obfuscated, to avoid exposing the database internal sequential IDs. Default value: `false`
6263
* __`OBFUSCATION_SALT`__: The salt string that is used to obfuscate the test IDs. The format shoud be a 2 byte hex string (e.g. `0x1234abcd`). If not specified, a random one will be generated.

docker/entrypoint.sh

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
#!/bin/bash
22

3+
echo "Setting up docker env..."
4+
echo "MODE: $MODE"
5+
echo "USE_NEW_DESIGN: $USE_NEW_DESIGN"
6+
echo "WEBPORT: $WEBPORT"
7+
echo "REDACT_IP_ADDRESSES: $REDACT_IP_ADDRESSES"
8+
echo "DB_TYPE: $DB_TYPE"
9+
echo "ENABLE_ID_OBFUSCATION: $ENABLE_ID_OBFUSCATION"
10+
311
set -e
4-
set -x
12+
#set -x
513

614
is_alpine() {
715
[ -f /etc/alpine-release ]
@@ -13,6 +21,10 @@ rm -rf /var/www/html/*
1321
# Copy frontend files
1422
cp /speedtest/*.js /var/www/html/
1523

24+
# Copy design switch files
25+
cp /speedtest/config.json /var/www/html/
26+
cp /speedtest/design-switch.js /var/www/html/
27+
1628
# Copy favicon
1729
cp /speedtest/favicon.ico /var/www/html/
1830

@@ -41,11 +53,30 @@ if [ "$MODE" == "backend" ]; then
4153
fi
4254

4355
# Set up index.php for frontend-only or standalone modes
44-
if [[ "$MODE" == "frontend" || "$MODE" == "dual" ]]; then
45-
cp -av /speedtest/frontend/* /var/www/html/
46-
elif [ "$MODE" == "standalone" ]; then
47-
cp -av /speedtest/frontend/* /var/www/html/
48-
echo '[{"name":"local","server":"/backend", "dlURL": "garbage.php", "ulURL": "empty.php", "pingURL": "empty.php", "getIpURL": "getIP.php", "sponsorName": "", "sponsorURL": "", "id":1 }]' > /var/www/html/server-list.json
56+
if [[ "$MODE" == "frontend" || "$MODE" == "dual" || "$MODE" == "standalone" ]]; then
57+
# Copy design files (switcher + both designs)
58+
cp /speedtest/index.html /var/www/html/
59+
cp /speedtest/index-classic.html /var/www/html/
60+
cp /speedtest/index-modern.html /var/www/html/
61+
62+
# Copy frontend assets directly to root-level subdirectories (no frontend/ parent dir)
63+
mkdir -p /var/www/html/styling /var/www/html/javascript /var/www/html/images /var/www/html/fonts
64+
cp -a /speedtest/frontend/styling/* /var/www/html/styling/
65+
cp -a /speedtest/frontend/javascript/* /var/www/html/javascript/
66+
cp -a /speedtest/frontend/images/* /var/www/html/images/
67+
cp -a /speedtest/frontend/fonts/* /var/www/html/fonts/ 2>/dev/null || true
68+
69+
# Copy frontend config files
70+
cp /speedtest/frontend/settings.json /var/www/html/settings.json 2>/dev/null || true
71+
if [ ! -f /var/www/html/server-list.json ]; then
72+
echo "no server-list.json found, create one for local host"
73+
# generate config for just the local server
74+
echo '[{"name":"local","server":"/backend", "dlURL": "garbage.php", "ulURL": "empty.php", "pingURL": "empty.php", "getIpURL": "getIP.php", "sponsorName": "", "sponsorURL": "", "id":1 }]' > /var/www/html/server-list.json
75+
fi
76+
fi
77+
# Configure design preference via config.json
78+
if [ "$USE_NEW_DESIGN" == "true" ]; then
79+
sed -i 's/"useNewDesign": false/"useNewDesign": true/' /var/www/html/config.json
4980
fi
5081

5182
# Apply Telemetry settings when running in standalone or frontend mode and telemetry is enabled

0 commit comments

Comments
 (0)