Skip to content

Commit 3a7dc31

Browse files
sstidlTimendusstefanstidlffgqodo-free-for-open-source-projects[bot]Copilot
authored
Implement the new design from #649 (#694)
* Implement fromScratch design * Add some documentation * enable new design in docker (no alpine yet) * merge fixed docker images * alpine docker added new design * fix #685 * Implement fromScratch design * Add some documentation * enable new design in docker (no alpine yet) * alpine docker added new design * fix #685 * fix database permissions alpine, remove baby * hide serverselector on only one server * Update frontend/styling/server-selector.css Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> * fix alpine image again * adjust settings.json in entrypoint so env vars are honored * Update frontend/javascript/index.js Co-authored-by: qodo-merge-for-open-source[bot] <189517486+qodo-merge-for-open-source[bot]@users.noreply.github.com> * 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 <12804296+sstidl@users.noreply.github.com> * Improve error handling and prevent infinite redirect loops Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * Update Dockerfiles and entrypoint to support design feature switch Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * 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 <12804296+sstidl@users.noreply.github.com> * Flatten frontend assets in Docker to eliminate frontend directory Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * fix: entrypoint settings & server-list disable entrypoint bash debug * add link to modern design --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> Co-authored-by: sstidl <sstidl@users.noreply.github.com> Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com> * add armv7 * reformat * Add GDPR_EMAIL environment variable for Docker deployments (#743) * Initial plan * Add GDPR_EMAIL environment variable for Docker deployments Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * Improve GDPR_EMAIL handling with proper escaping and loop Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * Refine GDPR_EMAIL processing - skip index.html and improve escaping Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * Optimize GDPR_EMAIL sed commands and improve escaping Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * Clarify sed escaping comment for GDPR_EMAIL Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * cleanup old EMAIL ENV Var * fix: line break in html prevented sed replacement * version 6.0.0pre1 * test: add mssql docker compose tests * Update Speedtest screen recording link in README * Filter unreachable servers from selector (newdesign UI) (#769) * Filter unreachable servers from selector (newdesign UI) * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * keep // servers in list Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix misleading comment --------- Co-authored-by: Lumi <lumi@openclaw.local> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Stefan Stidl <sti-github@stidl.com> * fix: server.json handling * Use server-list.json in classic frontend by default * Add configurable server list URLs to frontend and Docker --------- Co-authored-by: Timendus <mail@timendus.com> Co-authored-by: Stefan Stidl <stefan.stidl@ffg.at> Co-authored-by: qodo-merge-pro-for-open-source[bot] <189517486+qodo-merge-pro-for-open-source[bot]@users.noreply.github.com> Co-authored-by: qodo-merge-for-open-source[bot] <189517486+qodo-merge-for-open-source[bot]@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com> Co-authored-by: Stefan Stidl <sti-github@stidl.com> Co-authored-by: Lumi <lumi@openclaw.local> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent e1310d8 commit 3a7dc31

43 files changed

Lines changed: 3178 additions & 1087 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@
2727
"addEventListener": "readonly"
2828
},
2929
"rules": {
30-
"no-unused-vars": ["warn", { "args": "none" }],
30+
"no-unused-vars": [
31+
"warn",
32+
{
33+
"args": "none"
34+
}
35+
],
3136
"no-console": "off",
3237
"no-empty": "warn",
3338
"no-undef": "warn",
3439
"no-const-assign": "error"
3540
}
36-
}
41+
}

.prettierrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
"arrowParens": "avoid",
1010
"endOfLine": "lf",
1111
"htmlWhitespaceSensitivity": "ignore"
12-
}
12+
}

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: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@ RUN mkdir -p /speedtest/
1515

1616
# Copy sources
1717
COPY backend/ /speedtest/backend
18+
COPY frontend/ /speedtest/frontend
1819

1920
COPY results/*.php /speedtest/results/
2021
COPY results/*.ttf /speedtest/results/
2122

2223
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/
2328
COPY favicon.ico /speedtest/
2429

25-
COPY docker/servers.json /servers.json
26-
2730
COPY docker/*.php /speedtest/
2831
COPY docker/entrypoint.sh /
2932

@@ -35,6 +38,7 @@ ENV TELEMETRY=false
3538
ENV ENABLE_ID_OBFUSCATION=false
3639
ENV REDACT_IP_ADDRESSES=false
3740
ENV WEBPORT=8080
41+
ENV USE_NEW_DESIGN=false
3842

3943
# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
4044
STOPSIGNAL SIGWINCH

Dockerfile.alpine

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@ RUN mkdir -p /speedtest/
2929

3030
# Copy sources
3131
COPY backend/ /speedtest/backend
32+
COPY frontend/ /speedtest/frontend
3233

3334
COPY results/*.php /speedtest/results/
3435
COPY results/*.ttf /speedtest/results/
3536

3637
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/
3742
COPY favicon.ico /speedtest/
3843

39-
COPY docker/servers.json /servers.json
40-
4144
COPY docker/*.php /speedtest/
4245
COPY docker/entrypoint.sh /
4346

@@ -49,6 +52,7 @@ ENV TELEMETRY=false
4952
ENV ENABLE_ID_OBFUSCATION=false
5053
ENV REDACT_IP_ADDRESSES=false
5154
ENV WEBPORT=8080
55+
ENV USE_NEW_DESIGN=false
5256

5357
# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
5458
STOPSIGNAL SIGWINCH

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Works with mobile versions too.
2626
* Results sharing (optional)
2727
* Multiple Points of Test (optional)
2828

29-
![Screenrecording of a running Speedtest](https://speedtest.fdossena.com/mpot_v6.gif)
29+
![Screenrecording of a running Speedtest](https://speedtest.fdossena.com/mpot_v7.gif)
3030

3131
## Server requirements
3232

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.md

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# LibreSpeed
22

33
> by Federico Dossena
4-
> Version 5.5.1
4+
> Version 6.0.0pre1
55
> [https://github.com/librespeed/speedtest/](https://github.com/librespeed/speedtest/)
66
77
## Introduction
@@ -83,23 +83,7 @@ Log into your database using phpMyAdmin or a similar software and create a new d
8383

8484
Open `results/telemetry_settings.php` in a text editor. Set `$db_type` to either `mysql`,`postgresql`, `mssql` or `sqlite`.
8585

86-
If you chose to use SQLite, the default configuration stores the database at `__DIR__ . '/../../speedtest_telemetry.db'`, which places it two directories up from the `results/` folder. This is designed to keep the database **outside your web-accessible directory** for security.
87-
88-
**Critical Security Requirements**:
89-
1. **Web Server Configuration**: Configure your web server's document root to point to the application directory, NOT its parent. For example:
90-
- Install application files to: `/var/www/speedtest/`
91-
- Set Apache/nginx document root to: `/var/www/speedtest/` (NOT `/var/www/`)
92-
- Database will be at: `/var/www/speedtest_telemetry.db` (outside document root, not web-accessible)
93-
94-
2. **Alternative: Use Absolute Path**: For maximum security, especially if you cannot control the document root configuration, modify `$Sqlite_db_file` in `results/telemetry_settings.php` to use an absolute path completely outside your web directories:
95-
```php
96-
$Sqlite_db_file = '/var/lib/speedtest/speedtest_telemetry.db'; // or /opt/speedtest_data/speedtest_telemetry.db
97-
```
98-
Ensure the web server has write permissions to this directory.
99-
100-
3. **Verification**: After running at least one speed test or accessing `sanitycheck.php` (which creates the database), try accessing `http://yourserver/speedtest_telemetry.db` in a browser - you should get a 404 error. If the file downloads, your configuration is insecure. Note: The database file won't exist until the first test is recorded, so you'll get a 404 initially even with correct configuration.
101-
102-
SQLite doesn't require any additional configuration beyond setting a secure path and ensuring proper permissions.
86+
If you chose to use SQLite, you might want to change `$Sqlite_db_file` to another path where you want the database to be stored. Just make sure that the file cannot be downloaded by users. Sqlite doesn't require any additional configuration, you can skip the rest of this section.
10387

10488
If you chose to use MySQL, you must set your database credentials:
10589

@@ -872,9 +856,6 @@ s.setParameter("test_order","P_D_U");
872856

873857
This will point to our static files and set the test to only do ping/jitter, download and upload tests.
874858

875-
There is also an example to achieve this with nginx backend here:
876-
https://github.com/librespeed/speedtest/issues/375#issuecomment-3769211254
877-
878859
## Troubleshooting
879860

880861
These are the most common issues reported by users, and how to fix them. If you still need help, contact me at [info@fdossena.com](mailto:info@fdossena.com).

doc_docker.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ services:
3737
#ENABLE_ID_OBFUSCATION: "false"
3838
#REDACT_IP_ADDRESSES: "false"
3939
#PASSWORD:
40-
#EMAIL:
40+
#GDPR_EMAIL: "privacy@example.com"
4141
#DISABLE_IPINFO: "false"
4242
#IPINFO_APIKEY: "your api key"
4343
#DISTANCE: "km"
@@ -57,6 +57,8 @@ 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`
61+
* __`SERVER_LIST_URL`__: When set, both frontend designs load their server list from this URL instead of the generated or mounted `server-list.json`. This is useful if you want the containerized frontend to consume a remote shared server list.
6062
* __`TELEMETRY`__: Whether to enable telemetry or not. If enabled, you maybe want your data to be persisted. See below. Default value: `false`
6163
* __`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`
6264
* __`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.
@@ -70,7 +72,7 @@ Here's a list of additional environment variables available in this mode:
7072
* DB_USERNAME, DB_PASSWORD - credentials of the user with read and update permissions to the db
7173
* mssql - not supported in docker image yet (feel free to open a PR with that, has to be done in `entrypoint.sh`)
7274
* __`PASSWORD`__: Password to access the stats page. If not set, stats page will not allow accesses.
73-
* __`EMAIL`__: Email address for GDPR requests. Must be specified when telemetry is enabled.
75+
* __`GDPR_EMAIL`__: Email address displayed in the privacy policy for data deletion requests. If not set, the default placeholder text will be shown. This should be set to comply with GDPR requirements when running in production. Must be specified when telemetry is enabled.
7476
* __`DISABLE_IPINFO`__: If set to `true`, ISP info and distance will not be fetched from either [ipinfo.io](https://ipinfo.io) or the offline database. Default: value: `false`
7577
* __`IPINFO_APIKEY`__: API key for [ipinfo.io](https://ipinfo.io). Optional, but required if you want to use the full [ipinfo.io](https://ipinfo.io) APIs (required for distance measurement)
7678
* __`DISTANCE`__: When `DISABLE_IPINFO` is set to false, this specifies how the distance from the server is measured. Can be either `km` for kilometers, `mi` for miles, or an empty string to disable distance measurement. Requires an [ipinfo.io](https://ipinfo.io) API key. Default value: `km`
@@ -150,6 +152,12 @@ The test can be accessed on port 80.
150152

151153
The list of environment variables available in this mode is the same as [above in standalone mode](#standalone-mode).
152154

155+
If you want the Docker frontend to load its server list from another URL instead of `/servers.json`, set `SERVER_LIST_URL`:
156+
157+
```shell
158+
docker run -e MODE=frontend -e SERVER_LIST_URL="https://example.com/custom-server-list.json" -p 80:8080 -it ghcr.io/librespeed/speedtest
159+
```
160+
153161
#### Example Frontend mode
154162

155163
This command starts LibreSpeed in frontend mode, with a given `servers.json` file, and with telemetry, ID obfuscation, and a stats password and a persistant sqlite database for results:

0 commit comments

Comments
 (0)