426 Commits

Author SHA1 Message Date
Bill Zimmerman
c5f9c1593c Update release.yml 2025-08-21 18:59:55 +02:00
Bill Zimmerman
5cd39d6f0d Delete .github/workflows/torrent.yml 2025-08-21 17:03:04 +02:00
Bill Zimmerman
f0ceee0bcd Update release.yml 2025-08-21 17:02:47 +02:00
Bill Zimmerman
9db18fdefd Merge branch 'RaspAP:master' into master 2025-08-21 07:54:30 -07:00
Bill Zimmerman
7868c0d6c1 Create torrent.yml 2025-08-21 16:53:54 +02:00
Bill Zimmerman
e09e0590d7 Merge pull request #1935 from RaspAP/fix/custom-paths
Fix: Update class methods + js handler for custom path locations
2025-08-20 11:48:02 -07:00
Bill Zimmerman
771abe118e Merge pull request #1936 from RaspAP/fix/path-info-handling
Fix: Handle undefined PATH_INFO value
2025-08-20 11:47:35 -07:00
Bill Zimmerman
451c76afe8 Update torrent.yml 2025-08-20 18:58:33 +02:00
Bill Zimmerman
d21b1345bb Update torrent.yml 2025-08-20 18:50:43 +02:00
Bill Zimmerman
eca174a20b Create torrent.yml 2025-08-20 18:47:12 +02:00
billz
5319b9dbbd Handle undefined PATH_INFO value 2025-08-20 08:11:51 -07:00
billz
de9a3b1fc4 Update class methods + js handler for custom path locations 2025-08-19 17:17:27 -07:00
Bill Zimmerman
d9e00171b2 Merge pull request #1932 from RaspAP/maint/hostapd-json-cfg
Maintenance: Retrieve hostapd mode settings from defaults.json
2025-08-17 17:40:04 -07:00
billz
75577ecd1d Simplify wpa key management, ieee80211w settings 2025-08-17 14:46:15 -07:00
billz
c77fc254f6 Remove mappings, add hw_mode b settings 2025-08-17 14:45:06 -07:00
billz
d1c2e0d3ba Revise buildConfig() to parse settings from getDefaultNetValue 2025-08-17 13:01:58 -07:00
billz
7976d77ac1 Define hostapd settings per hw_mode 2025-08-17 13:00:19 -07:00
billz
c3cc4ff9db Update plugins submodule pointer 2025-08-13 09:49:46 -07:00
billz
b1d776aa64 Update release version 2025-08-12 13:32:53 -07:00
Bill Zimmerman
c7194e2e26 Merge pull request #1927 from RaspAP/fix/hostapd-ini
Fix: Return expected datatype from getHostapdIni()
2025-08-12 13:30:42 -07:00
Bill Zimmerman
2514b0a569 Merge pull request #1926 from RaspAP/fix/js-vendor-assets
Fix: Re-add js/vendor ignored in .gitignore
2025-08-12 13:30:28 -07:00
billz
b23084fe7b Fix: Return expected datatype from getHostapdIni() 2025-08-12 13:26:00 -07:00
billz
e39d35a395 Re-add js/vendor ignored in .gitignore 2025-08-12 13:11:59 -07:00
billz
cbc6221420 Update release version 2025-08-12 10:34:24 -07:00
Bill Zimmerman
e855d12949 Merge pull request #1907 from RaspAP/maint/hostapd-refactor
Maintenance: Refactor legacy procedural code into class methods
2025-08-02 14:31:56 -07:00
billz
7f2eb6e88f Coalesce dhcp-option=6 lines, prevents invalid config 2025-08-02 13:13:41 -07:00
billz
63491b17d6 Run wpa_supplicant in background mode (-B) 2025-08-02 13:12:39 -07:00
Bill Zimmerman
5fbc94c319 Merge pull request #1921 from RaspAP/dependabot/npm_and_yarn/brace-expansion-1.1.12
Bump brace-expansion from 1.1.11 to 1.1.12
2025-07-31 15:25:52 -07:00
dependabot[bot]
58e0867c1e Bump brace-expansion from 1.1.11 to 1.1.12
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.11 to 1.1.12.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-30 18:00:21 +00:00
billz
3922832b53 Fix: update w/ CSRF::hiddenField() 2025-07-28 15:56:31 -07:00
billz
dadc4e4fb4 Refactor setKnownStationsWPA(), add helper method addWpaNetwork() 2025-07-28 15:56:01 -07:00
billz
3c1d4325f2 Implement dnsmasq restart button 2025-07-28 13:34:45 -07:00
billz
f30abc4bd7 Remove debug output 2025-07-28 13:34:26 -07:00
billz
a13e1b8804 Populate static-lease container from jsonData.dhcpHost 2025-07-28 12:56:36 -07:00
billz
b2b52c2c36 Merge branch 'master' into maint/hostapd-refactor 2025-07-28 09:09:40 -07:00
Bill Zimmerman
cfef857d34 Merge pull request #1918 from RaspAP/feat/bootstrap-alert-setting
Feature: User-definable alert timeout option
2025-07-26 10:56:01 -07:00
Bill Zimmerman
1b9d522bd8 Merge pull request #1917 from RaspAP/fix/wg-killswitch
Append PostUpEx/PreDown rules to WG config
2025-07-26 05:41:31 -07:00
billz
59e7a9d859 Add messages to en_US locale 2025-07-25 21:15:16 -07:00
billz
01a441c687 Separate system settings save functions, read alert_timeout 2025-07-25 21:12:42 -07:00
billz
4f90c035ca Move alert message options to theme tab 2025-07-25 21:10:20 -07:00
billz
f558d02e68 Add messages to en_US locale 2025-07-25 19:37:34 -07:00
billz
2967f5b692 Implement user-definable alert timeout option 2025-07-25 19:34:31 -07:00
Bill Zimmerman
a005ba30b9 Merge pull request #1916 from ruralmeltdown/1911_2025-07-24_add-auto-close-alerts
add auto-close to Bootstrap alerts
2025-07-25 12:03:41 -07:00
billz
bb76eb86a4 Append PostUpEx/PreDown rules to WG config 2025-07-25 11:55:18 -07:00
ruralmeltdown
33476098c1 add auto-close to Bootstrap alerts 2025-07-24 07:36:30 -07:00
Bill Zimmerman
6e749dd2d6 Merge pull request #1914 from ruralmeltdown/1912_2025-07-23_navigation-menu-persistence
comment out sbadmin code for sidebar overlay
2025-07-24 01:13:25 +02:00
ruralmeltdown
522b204bb9 comment out sbadmin code for sidebar overlay 2025-07-23 15:21:02 -07:00
billz
e0e236faa2 Merge branch 'master' into maint/hostapd-refactor 2025-07-21 00:26:32 -07:00
billz
c19bd60241 Fix: set return value (bool) 2025-07-20 14:46:33 -07:00
billz
7a7bdda708 Delegate dnsmasq + dhcpcd config build/save to manager classes 2025-07-20 10:02:58 -07:00
billz
7cc436fbaa Add missing param in dnsmasq->saveConfig() 2025-07-20 10:00:15 -07:00
billz
87f55c8b1e Standardize array return/input type for build + save config 2025-07-20 09:58:56 -07:00
billz
98922434f2 Added buildConfigEx(), migrated from legacy controller 2025-07-20 08:07:51 -07:00
billz
83ab53dd87 WIP: refactored to use dnsmasq + dhcpcd manager classes 2025-07-20 06:39:59 -07:00
billz
1e2f77abcb Added buildEx, buildDefault, saveConfigDefault, remove, scanConfigDir methods 2025-07-20 06:38:33 -07:00
billz
8f19d759f2 Consolidate functions in their respective dhcpcd + dnsmasq classes 2025-07-20 06:36:43 -07:00
billz
ed1938d10b Migrate validate, remove + removeIface methods from global function defs 2025-07-20 06:35:19 -07:00
billz
9df3baa5f1 Minor: standardize comment header, declare strict_types=1 2025-07-20 02:08:33 -07:00
billz
cbc6ee74c3 Consolidate non-hostapd concerns under HotspotService class 2025-07-20 02:07:53 -07:00
billz
cf32a4ba01 Implement saveSettings(), consolidate hotspot functions + static methods 2025-07-20 02:06:40 -07:00
billz
636e04fa78 Migrate non-hostapd methods to HotspotService class 2025-07-20 02:05:05 -07:00
billz
5f4469ab32 Update legacy handler w/ dhcpcdManager->getInterfaceConfig() 2025-07-20 01:49:31 -07:00
billz
5a57d542c5 Normalize state flags, try-catch blocks for method calls, prune legacy dhcpcd routines 2025-07-19 15:11:37 -07:00
billz
3fe4990cfd Implement buildConfig, saveConfig, getInterfaceConfig methods 2025-07-19 15:08:13 -07:00
billz
92f9cf745e Add getHostapdIni, countHostapdConfigs methods 2025-07-19 15:04:16 -07:00
billz
c5ff6912ea Set properties from class methods, process txpower input 2025-07-19 06:07:54 -07:00
billz
e12be86c8c Add static methods for 802.11 standards, resolveHwMode() 2025-07-19 06:07:54 -07:00
billz
5cd07a83a9 Revise /sbin/iw permissions scope 2025-07-19 06:07:54 -07:00
billz
126f64a793 Fix PHP warning
Fix PHP warning
2025-07-19 06:07:52 -07:00
billz
3b352b12d8 WIP: migrate functions to class methods, refactor saveHostAPDConfig() 2025-07-19 06:07:52 -07:00
billz
b9642371e0 Use HostapdValidator, add deriveInterfaces, deriveModeStates, iwRegSet methods 2025-07-19 06:07:52 -07:00
billz
9dc6209b47 Minor: comment block
Minor: comment block
2025-07-19 06:07:49 -07:00
billz
9d03517896 Add getIfaceMetric public method 2025-07-19 03:17:17 -07:00
billz
fa38ac6153 Initial commit 2025-07-19 03:16:47 -07:00
billz
dd3b300931 Instantiate WiFiManager, update w/ $wifi->getSignalBars() 2025-07-19 00:26:53 -07:00
billz
da6f469982 Implement buildConfig() 2025-07-19 00:25:46 -07:00
billz
a91e441073 Implement persistHostapdIni() 2025-07-19 00:25:18 -07:00
billz
f1ced91811 Update w/ WiFiManager class method calls 2025-07-19 00:24:41 -07:00
billz
3ad5a98798 Replace global wifi_functions with WiFiManager class 2025-07-19 00:24:01 -07:00
billz
094ebdb85f Replace procedural code w/ dnsmasq->buildConfig, hostapd->persistHostapdIni 2025-07-19 00:23:14 -07:00
billz
dea3e7c485 Modify select to use selected_hw_mode from config 2025-07-19 00:19:24 -07:00
billz
4e55f5a97f Initial commit 2025-07-19 00:18:18 -07:00
billz
619bfdc04d Create getConfig(), saveConfig() class methods 2025-07-18 14:44:52 -07:00
billz
2b2a76c512 Update w/ reload dnsmasq.service 2025-07-18 14:43:47 -07:00
billz
b293355eac Replace procedural code w/ HostapdManager, DnsmasqManager method calls 2025-07-18 14:43:25 -07:00
Bill Zimmerman
2f9a2dfa92 Merge pull request #1906 from RaspAP/fix/persist-static-leases
Fix: Persist multiple dhcp-* values on dnsmasq.conf save
2025-07-18 16:32:38 +02:00
billz
fcca855c44 Use ParseConfig() to read/write multiple dhcp-* values 2025-07-18 01:51:17 -07:00
billz
cc4370151f Fix: Persist dhcp-host option to dnsmasq cfg 2025-07-18 01:29:13 -07:00
Bill Zimmerman
2c4dbb87ba Merge pull request #1893 from tulik/fix/escape-color-output-compute-php
fix(color): add validation for CSS color formats in getColorOpt function
2025-07-17 14:53:24 +02:00
billz
99b46ce086 Update BACKERS.md 2025-07-16 23:48:04 -07:00
Bill Zimmerman
ee8d32383b Merge pull request #1905 from RaspAP/maint/js-assets-reorg
Maintenance: Reorg JavaScript assets
2025-07-17 08:47:20 +02:00
Lukasz Tulikowski
bad782deda fix(color): sanitize color output in SVG and CSS files to prevent XSS vulnerabilities 2025-07-16 10:22:30 +02:00
billz
e31ccd09e8 Group vendor related js into js/vendor 2025-07-16 01:14:38 -07:00
billz
134f80ada8 Replace monolithic custom.js with ajax + ui 2025-07-16 01:14:15 -07:00
billz
5040507750 Load scripts from js/vendor for sanity 2025-07-16 01:13:23 -07:00
billz
02b31a0254 Split custom.js into ajax/ + ui/ for sanity 2025-07-16 01:12:56 -07:00
billz
807d903c8a Add app/js/plugins/ to .gitignore 2025-07-16 01:12:04 -07:00
Lukasz Tulikowski
478ba9973f fix(color): enhance regex pattern for CSS color validation in getColorOpt function 2025-07-16 10:01:13 +02:00
billz
83a3057e7f Update BACKERS.md 2025-07-15 02:07:42 -07:00
billz
f29f0f2b53 Update release version 2025-07-15 00:11:37 -07:00
Bill Zimmerman
1382cdda33 Merge pull request #1900 from RaspAP/feat/insiders-2nd-edition
Feature: Insiders 2nd Edition goal reached 🎉
2025-07-15 09:08:19 +02:00
Bill Zimmerman
8f37b35088 Merge pull request #1904 from RaspAP/fix/static-lease-mac
Apply input mask to MAC address field using [0-9a-fA-F] pattern
2025-07-15 08:02:26 +02:00
billz
b374befa8e Apply input mask to MAC address field using [0-9a-fA-F] pattern 2025-07-14 22:57:59 -07:00
billz
378e04939e Merge branch 'master' into feat/insiders-2nd-edition 2025-07-14 22:37:52 -07:00
Bill Zimmerman
bd3ac1b611 Merge pull request #1902 from RaspAP/feat/wifi-scanning
Feature: Refactor nearbyWifiStations() network scanning
2025-07-14 21:22:25 +02:00
billz
349c5af574 Adjust top position of loading-spinner 2025-07-14 09:30:41 -07:00
billz
918f7daa74 Refactor nearbyWifiStations() to use iw instead of wpa_cli 2025-07-14 09:30:10 -07:00
billz
6f380299db Update sudoers w/ wireguard permissions 2025-07-14 07:08:35 -07:00
billz
c33522b015 Clarify gateway/nogateway option w/ tooltip, update en_US messages 2025-07-14 06:31:17 -07:00
billz
e126f3f664 Update social link 2025-07-14 05:50:43 -07:00
billz
c0df273c36 Refactor hostapd cfg parse/write (source: raspap-insiders) 2025-07-14 03:18:53 -07:00
billz
7994fa3c33 Update security options w/ 802.11w, printable sign link 2025-07-14 03:18:53 -07:00
billz
7fceaf536c Suppress call to loadCurrentSettings (deprecated) 2025-07-14 03:18:53 -07:00
billz
7a0b93a0e8 Update networking w/ speedtestUI.js, diagnostics tab 2025-07-14 03:18:53 -07:00
billz
c21d5a1790 Update w/ checkHTTPAccess() 2025-07-14 03:18:53 -07:00
billz
4f57e259dd Define RASPI_IPTABLES_CONF, ACCESS_CHECK_* 2025-07-14 03:18:53 -07:00
billz
5c979424f3 Initial commit (source: raspap-insiders) 2025-07-14 03:18:53 -07:00
billz
51d528fd42 Add $extraFooterScripts param to DisplayNetworkingConfig() 2025-07-14 03:18:53 -07:00
billz
698b8bf809 Update w/ versioned Librespeed CSS + JS 2025-07-14 03:18:53 -07:00
billz
b5e79b9148 Enable wg kill switch 2025-07-14 03:18:39 -07:00
billz
810218b67e Add speedtest dependencies 2025-07-14 02:17:01 -07:00
billz
80a5d97eee Update release version 2025-07-12 00:37:17 -07:00
Bill Zimmerman
6ef5d10fe8 Merge pull request #1899 from RaspAP/fix/locales-php74-compatibility
Fix: Replace match in detectBrowserLocale() for PHP 7.4
2025-07-11 20:27:36 +02:00
billz
96aa833477 Fix: Replace match in detectBrowserLocale() for PHP 7.4 2025-07-11 10:53:16 -07:00
Bill Zimmerman
ee916dadac Merge pull request #1897 from RaspAP/maint/cache-busting
Maintenance: Add cache-busting versioning to JS + CSS assets
2025-07-11 17:44:07 +02:00
billz
c4f29443c4 Add cache-busting versioning to CSS and JS assets 2025-07-11 02:30:44 -07:00
billz
eee8575d3f Initial commit 2025-07-11 02:29:54 -07:00
Bill Zimmerman
99cab5a06e Merge pull request #1896 from RaspAP/maint/deprecated-files
Maintenance: Cleanup obsolete/deprecated files
2025-07-11 11:07:02 +02:00
Bill Zimmerman
e4e8204660 Merge pull request #1895 from RaspAP/feat/hostapd-service
Feature: Install hostapd@.service
2025-07-11 11:06:38 +02:00
billz
7fc7b25479 Cleanup obsolete/deprecated files 2025-07-10 23:57:36 -07:00
billz
697c622f76 Return _install_status 0 2025-07-10 22:26:53 -07:00
billz
780803b0ec Create _install_raspap_hostapd() 2025-07-10 12:18:22 -07:00
billz
ad22fb693b Initial commit 2025-07-10 12:07:51 -07:00
Lukasz Tulikowski
3152e8c288 fix(color): add validation for CSS color formats in getColorOpt function 2025-07-08 20:26:05 +02:00
Bill Zimmerman
7883514f40 Merge pull request #1890 from RaspAP/maint/translations
Maintenance: Update Korean, German + Spanish locales
2025-07-04 08:20:48 +02:00
billz
345c331b10 fix(i18n): improve German translation for limited user privileges - thx @Noschvie 2025-07-03 00:58:14 -07:00
billz
ac9a39b5be Update w/ adjective eingeschränkten - thanks @Noschvie 2025-07-02 23:55:39 -07:00
billz
6de96dad14 Update translations + compile .mo files 2025-07-02 23:26:46 -07:00
Bill Zimmerman
eb8d81e590 Update README.md 2025-06-29 21:16:23 +02:00
billz
5822914890 Update release version 2025-06-25 01:39:44 -07:00
Bill Zimmerman
bba2f67931 Merge pull request #1887 from YichaoXu/master
fix: mitigate UUF vulnerability by escaping entity with escapeshellarg
2025-06-25 10:34:48 +02:00
Bill Zimmerman
2a4f2f356c Merge pull request #1885 from RaspAP/feat/system-diskuse
Feature: System disk storage use
2025-06-24 13:38:47 +02:00
Bill Zimmerman
665520234a Merge pull request #1884 from RaspAP/fix/sanitize-locale
Fix: Sanitize user input, add detectBrowserLocale()
2025-06-24 13:37:21 +02:00
Bill Zimmerman
65be23a1db Merge pull request #1881 from RaspAP/feat/blocklists-extended
Feature: Extended blocklist management
2025-06-24 13:22:53 +02:00
Sam Hsu
eb53c46c33 fix: mitigate UUF vulnerability by escaping entity with escapeshellarg 2025-06-24 00:27:38 -07:00
billz
1eed833909 Update en_US locale + compile .mo 2025-06-21 01:57:41 -07:00
billz
c222f9cd4f Add storage used progress bar to system stats 2025-06-21 01:55:53 -07:00
billz
3b35f5a0c6 Add usedDisk() public method 2025-06-21 01:55:22 -07:00
billz
72d7028e25 Fetch disk storage from system->usedDisk, create getResourceStatus() 2025-06-21 01:54:47 -07:00
billz
f514f5a12e Sanitize user input w/ $validLocales, add detectBrowserLocale() 2025-06-21 01:19:17 -07:00
billz
3dca072b4c Update README w/ wg, tailscale + ovpn doc links 2025-06-19 12:27:17 -07:00
billz
f802c825f6 Minor: whitespace 2025-06-18 00:33:37 -07:00
billz
c0f496cf07 Revise updateBlocklist() w/ more robust error handling 2025-06-18 00:31:47 -07:00
billz
116704c59b Replace hardcoded path w/ $scriptPath, add file check 2025-06-18 00:10:46 -07:00
billz
e9742a5252 Enhance/update blocklists w/ nested metadata 2025-06-17 07:02:29 -07:00
billz
2aaf1eca07 Refactor ajax handler, separate config + logic 2025-06-17 07:01:17 -07:00
billz
575876406c Update blocklistProviders, extract names from json 2025-06-17 06:59:14 -07:00
Bill Zimmerman
9c4cee49cd Merge pull request #1875 from Dhanus3133/feat/insiders-build
Add common script to configure public and Insiders builds
2025-06-07 15:41:08 +02:00
Dhanus
be0722f20a feat(build): Add dynamic configuration for Insiders stage 2025-06-07 13:02:56 +05:30
billz
8dc0308a1d Update release version 2025-06-05 01:43:05 -07:00
Bill Zimmerman
f4adbc1725 Merge pull request #1872 from RaspAP/fix/plugin-installers-packages
Fix installDependencies() to correctly handle packageList array
2025-06-04 17:42:30 +02:00
billz
475ac70837 Fix installDependencies() to correctly handle packageList array 2025-06-04 00:59:35 -07:00
Bill Zimmerman
9cfccc6e69 Merge pull request #1871 from RaspAP/maint/raspap-fonts
Maintenance: Update RaspAP custom font w/ ra-torproxy
2025-06-03 12:09:38 +02:00
billz
1ab9779b13 Update RaspAP custom font w/ ra-torproxy 2025-06-03 01:51:51 -07:00
Bill Zimmerman
1b39a7a1bf Update BACKERS.md 2025-05-31 20:26:18 +02:00
Bill Zimmerman
e65a29f911 Merge pull request #1862 from RaspAP/maint/installer-color
Update install loader with ANSI_AQUA theme color
2025-05-29 07:50:16 +02:00
Bill Zimmerman
98f11477b0 Merge pull request #1867 from RaspAP/fix/dashboard-wlan
Update getWirelessClients() w/ interface parameter
2025-05-25 08:39:20 +02:00
billz
290cad8107 Update getWirelessClients() w/ interface parameter 2025-05-24 10:15:58 -07:00
billz
8b3694fd5c Substitute ANSI_GREEN for ANSI_LT_AQUA 2025-05-24 10:10:58 -07:00
billz
452f4aefbd Update install loader w/ ANSI_AQUA theme color 2025-05-17 00:56:28 -07:00
Bill Zimmerman
231b4c3326 Merge pull request #1859 from RaspAP/fix/ajax-installer-csrf
Fix: Ajax update use of legacy CSRF token handler
2025-05-15 07:46:42 +02:00
billz
e55d24db66 Log generic error message on token verify fail 2025-05-14 00:57:19 -07:00
billz
2a2b429e75 Handle missing tokens gracefully, support multiple state-changing HTTP methods 2025-05-14 00:54:38 -07:00
billz
a1ca7e861f Resolve error related to legacy CSRF token handler 2025-05-13 07:49:46 -07:00
Bill Zimmerman
992f999a7b Update issue_form.yml 2025-05-13 10:16:56 +02:00
billz
be260d4b39 Update release version 2025-05-13 00:18:31 -07:00
Bill Zimmerman
ce340cac67 Merge pull request #1856 from RaspAP/maint/translation-update
Update en_US locale messages + compile .mo
2025-05-13 08:57:55 +02:00
billz
540affbba1 Update en_US locale messages + compile .mo 2025-05-12 23:51:11 -07:00
Bill Zimmerman
1ee80b0572 Merge pull request #1853 from RaspAP/fix/dhcpcd-handling
Refactor dhcpcd.conf handling w/ updateDhcpcdConfig()
2025-05-13 08:48:48 +02:00
billz
0dc9e24f65 Refactor dhcpcd.conf handling w/ updateDhcpcdConfig() 2025-05-12 07:16:32 -07:00
Bill Zimmerman
98e753ffa1 Merge pull request #1851 from RaspAP/feat/plugin-installer-ex
Extends PluginInstaller w/ setFilePermissions()
2025-05-12 06:50:19 +02:00
billz
2c695225cd Extends PluginInstaller w/ setFilePermissions() 2025-05-11 02:57:58 -07:00
billz
2d39c96153 Minor update to formatProperty() 2025-05-11 01:21:08 -07:00
Bill Zimmerman
ca8224474b Merge pull request #1850 from RaspAP/maint/raspap-fonts
Maintenance: Update RaspAP custom font
2025-05-11 06:52:39 +02:00
billz
701f6d7e3e Adjust ra-tailscale .sb-nav margin-bottom 2025-05-10 21:44:59 -07:00
billz
26293d1e3b Update RaspAP custom font 2025-05-10 07:36:55 -07:00
Bill Zimmerman
02852bfe55 Merge pull request #1843 from RaspAP/fix/servicestart-default
Fix: Set default interface value if undefined
2025-05-03 17:15:32 +02:00
billz
ef073ef6ec Set default interface value if undefined 2025-05-02 23:49:31 -07:00
Bill Zimmerman
219c40b6fd Merge pull request #1841 from RaspAP/fix/c-devtoolchain
Fix: Install build-essential to provide C standard library headers
2025-05-01 09:40:49 +02:00
billz
cc1832a296 Install build-essential to provide C standard library headers 2025-05-01 00:33:12 -07:00
billz
16afef3e13 Update release version 2025-04-29 06:27:34 -07:00
Bill Zimmerman
3b172715bb Merge pull request #1828 from RaspAP/feat/net-activity-led
Feature: Network activity LED indicator
2025-04-29 09:36:49 +02:00
Bill Zimmerman
e312e0dd99 Merge pull request #1839 from RaspAP/maint/aqua-favicons
Update favicons to latest aqua scheme
2025-04-29 09:33:09 +02:00
billz
f33c6af4ee Update site.webmanifest 2025-04-28 23:32:35 -07:00
billz
a8bb529c3d Update favicons to latest aqua scheme 2025-04-28 23:30:31 -07:00
billz
9528927cca Check for presence of gcc, install if needed w/ apt 2025-04-28 01:44:22 -07:00
billz
384e1df1b7 Minor: echo output 2025-04-28 00:58:24 -07:00
billz
0ad82da51c Default to interface value in /etc/raspap/hostapd.ini 2025-04-28 00:47:16 -07:00
billz
5d8b71b768 Delegate raspap-network-activity@*.service control to raspapd.service 2025-04-28 00:14:04 -07:00
billz
12c28f3f60 Update to stop/start raspap-network-activity@.service 2025-04-28 00:12:59 -07:00
billz
89dfa02d96 Minor: comments 2025-04-27 09:12:17 -07:00
billz
b34f96d351 Update ExecStart path to /usr/local/bin/raspap-network-monitor %i 2025-04-27 09:09:43 -07:00
billz
f30ff24ed4 Update _enable_network_activity_monitor() compile raspap-network-monitor.c 2025-04-27 09:08:51 -07:00
billz
b900c401fe Initial commit 2025-04-27 09:07:58 -07:00
billz
de7e36cb8c Add app/net_activity to .gitignore 2025-04-27 01:36:01 -07:00
billz
cc215530d3 Stop raspap-network-activity@*.service, start service template w/ iface 2025-04-27 01:35:21 -07:00
billz
d7428e40e3 Create _enable_network_activity_monitor() + _symlink_net_activity() 2025-04-27 01:34:22 -07:00
billz
2d7d521501 Add start/stop raspap-network-activity@*.service 2025-04-27 01:33:52 -07:00
billz
1bf91277da Rename systemd unit file template @.service 2025-04-27 01:33:19 -07:00
billz
08528c8843 Merge branch 'master' into feat/net-activity-led 2025-04-25 00:40:23 -07:00
Bill Zimmerman
36610ac519 Merge pull request #1833 from no-sec-marko/fix/rce-hostapd
Add interface validation and improve shell argument escaping
2025-04-25 08:45:37 +02:00
Bill Zimmerman
eda490e452 Merge pull request #1835 from RaspAP/feat/wg-iptables-iface
Feature: User-configurable interface for WG PostUp/PostDown rules
2025-04-25 08:44:24 +02:00
billz
f614fa80dc Update wg messages + compile .mo 2025-04-24 04:51:18 -07:00
billz
a09550bf73 Update template w/ interface select for PostUp/PostDown rules 2025-04-24 04:47:43 -07:00
billz
6899389a81 Read/set wgInterface, update SaveWireGuardUpload(), enumerate interfaces 2025-04-24 04:46:41 -07:00
Marko Winkler
52a4acced8 Add interface validation and improve shell argument escaping
- Introduced validateInterface() to ensure only existing network interfaces are used
- Used escapeshellarg() for user-supplied interfaces in shell commands (iw, iwgetid)
- Replaced direct usage of $_POST['interface'] with validated fallback to RASPI_WIFI_AP_INTERFACE
- Improved code readability by reducing redundant assignments
2025-04-23 21:31:38 +02:00
billz
3cb2e67387 Cleanup unused classes + setAttribute defs 2025-04-20 03:38:45 -07:00
billz
5c7ccfb64f Remove --interface wlan0 arugment (set by default) 2025-04-20 03:36:39 -07:00
billz
1ab3906632 Initial commit 2025-04-19 06:07:08 -07:00
billz
73df916184 Add hostapd-led to fas class in template 2025-04-19 06:06:28 -07:00
billz
97b846b2b6 Revise updateActivityLED() to handle multiple hostapd-led classes 2025-04-19 06:04:59 -07:00
billz
12b343b570 Added led-pulse animation + keyframes, #hostapd-led 2025-04-19 06:04:01 -07:00
billz
03e2b853be Remove debug output, format comments 2025-04-18 12:37:50 -07:00
billz
6fe48d0e4e Update status panel w/ hostapd-led ID 2025-04-18 12:33:14 -07:00
billz
1d80b5090d Create updateActivityLED() 2025-04-18 12:32:02 -07:00
billz
658e931aa6 Initial commit 2025-04-18 12:30:59 -07:00
billz
c3175459ab Update release version 2025-04-17 05:44:30 -07:00
Bill Zimmerman
3bf682d752 Merge pull request #1811 from RaspAP/feat/csrf-tokenizer
Feature: CSRF tokenizer class
2025-04-17 14:32:58 +02:00
Bill Zimmerman
9e0801aa0c Merge pull request #1825 from RaspAP/fix/custom-blocklists
Fix: Check for presence of custom blocklist
2025-04-17 10:07:12 +02:00
billz
a21b0a8f99 Check for presence of custom blocklist in RASPI_ADBLOCK_LISTPATH 2025-04-16 23:33:13 -07:00
billz
5f1b16bc74 Remove instantiation of $token 2025-03-26 11:19:00 -07:00
billz
deba5e1e74 When session token expires, redirect instead of returning a 500 error 2025-03-26 09:51:39 -07:00
billz
0960e8bac9 Add CSRF protection include 2025-03-26 04:05:39 -07:00
billz
4a4506a913 Update with CSRF::hiddenField() 2025-03-26 04:03:37 -07:00
billz
bfab3d7441 Revise CSRFValidate() 2025-03-26 04:02:48 -07:00
billz
47d7c121de Generate new session id on logout() 2025-03-26 04:01:09 -07:00
billz
484b89718a Add validateRequest(), token check outside class 2025-03-26 04:00:34 -07:00
billz
d6c8ac32a7 Fix call to class method 2025-03-25 14:05:34 -07:00
billz
3d6b4e1f15 Simplify getVpnManaged check with ternary operator 2025-03-25 14:04:29 -07:00
billz
8d482845b0 Replace explicit token instantiation w/ includes/CSRF.php 2025-03-25 14:02:51 -07:00
billz
a1550d8049 Refactor templates to use CSRF facade 2025-03-25 14:00:24 -07:00
billz
5584e3b72c Initial commit 2025-03-25 13:58:52 -07:00
billz
b0ba029c66 Revert "Accept $token object, pass to renderTemplate()"
This reverts commit b3c6178274.
2025-03-25 13:23:58 -07:00
billz
48e492bf10 Ensure a CSRF token exists in session 2025-03-25 06:49:18 -07:00
billz
821ac9c1f8 Numerous fixes to class methods 2025-03-25 05:26:53 -07:00
billz
49cb3911b8 Fixup misnamed method getVpnManged -> getVpnManaged 2025-03-25 05:25:11 -07:00
billz
8ad582b3b2 Instantiate ExceptionHandler, CSRFTokenizer objects 2025-03-25 05:23:52 -07:00
billz
bbf1caf777 Update with $token->CSRFTokenFieldTag() 2025-03-25 05:23:06 -07:00
billz
b3c6178274 Accept $token object, pass to renderTemplate() 2025-03-25 05:22:29 -07:00
billz
a5907d8f7f Class loading handled by autoloader, objects instantiated by index.php 2025-03-25 05:21:35 -07:00
billz
8569c2b4d5 Remove CSRF related functions (made obsolete by Token class) 2025-03-25 05:17:58 -07:00
billz
2a70f6ee11 Replace csrf and explicit HTTPAuth includes with autoload.php 2025-03-25 05:15:34 -07:00
billz
c8b0408bd5 Refactor + fixup autoloader 2025-03-25 05:14:29 -07:00
Bill Zimmerman
125ae7a39a Merge pull request #1810 from RaspAP/fix/php74-compatibility
Fix: php7.4 compatibility
2025-03-25 08:36:03 +01:00
billz
20fe5fc5a7 Fix: php7.4 compatibility 2025-03-24 23:58:30 -07:00
billz
168ed2448f Update release version 2025-03-24 01:51:08 -07:00
Bill Zimmerman
a2b8dfe551 Merge pull request #1806 from RaspAP/fix/fetch-update-response
Fix: Revise fetchUpdateResponse() and modal dialog
2025-03-24 09:45:33 +01:00
billz
4aee1e49d9 Revise fetchUpdateResponse() without endPolling flag 2025-03-24 01:37:16 -07:00
billz
16d92bb486 Set fa-check visible for updateStep1 2025-03-24 01:35:40 -07:00
billz
de376d04d1 Initial commit 2025-03-23 23:13:29 -07:00
Bill Zimmerman
d730c174d4 Update README.md 2025-03-24 06:29:58 +01:00
billz
c798f5fd69 Update BACKERS.md 2025-03-23 22:28:03 -07:00
Bill Zimmerman
3a73916206 Merge pull request #1805 from RaspAP/fix/connection-type
Update getConnectionType() regexes to match MAC-derived names
2025-03-24 06:24:33 +01:00
billz
f46be0139e Update getConnectionType() regexes to match MAC-derived names 2025-03-23 10:52:46 -07:00
Bill Zimmerman
a6295aef6e Merge pull request #1799 from RaspAP/feat/dashboard-devices
Feature: Dashboard + System device illustrations
2025-03-23 17:51:20 +01:00
Bill Zimmerman
556c2be855 Merge pull request #1802 from RaspAP/fix/dhcp-settings-focus
Fix: Links to dhcp client-list tab
2025-03-23 17:51:05 +01:00
Bill Zimmerman
80e8178384 Update issue_form.yml 2025-03-23 17:13:44 +01:00
billz
b1c429f404 Fix: remove link to client-list tab 2025-03-23 08:45:21 -07:00
Bill Zimmerman
f0b992b9be Merge pull request #1800 from RaspAP/fix/band-association
Fix: Update 802.11n band associations
2025-03-23 15:23:45 +01:00
billz
084b2e1268 Update 802.11n bands, resolves #1797 2025-03-23 07:22:15 -07:00
billz
0fd60e3730 Update device illustration w/ w/ $deviceImage 2025-03-23 07:18:55 -07:00
billz
cfc6644087 Merge branch 'master' into feat/dashboard-devices 2025-03-23 07:08:04 -07:00
Bill Zimmerman
2cd4abc3c2 Merge pull request #1798 from RaspAP/feat/tabbed-dashboard
Feature: Tabbed dashboard
2025-03-23 15:01:12 +01:00
Bill Zimmerman
8bd5b1b988 Merge pull request #1796 from RaspAP/feat/auth-enhancements
Feature: Auth enhancements
2025-03-23 15:00:57 +01:00
billz
2b8f7fd6d8 Initial commit 2025-03-23 02:18:50 -07:00
billz
b99addef4a Replace static device illustration w/ $deviceImage 2025-03-23 01:30:32 -07:00
billz
96ada80ce1 Update RPi models from latest revision codes 2025-03-23 01:28:51 -07:00
billz
5d33c79369 Return default if revision type not matched 2025-03-23 01:25:45 -07:00
billz
645ab89437 Update w/ getDeviceImage() 2025-03-23 01:22:54 -07:00
billz
62b342cdbb Set device image w/ getDeviceImage($revision) 2025-03-23 01:22:26 -07:00
billz
c235a4ff16 Create zero.php, move under /devices 2025-03-23 01:21:28 -07:00
billz
a24516a4d5 Tweak divDBChartBandwidthhourly 2025-03-23 00:33:27 -07:00
billz
483b1fc27d Split single template into tabs 2025-03-23 00:28:31 -07:00
billz
4d0de82986 Add &$extraFooterScripts to support data usage graph 2025-03-23 00:27:39 -07:00
billz
85663341eb Initial commit 2025-03-23 00:26:48 -07:00
billz
734043dee6 Define button object, add logout btn 2025-03-22 03:17:23 -07:00
billz
af2927f05b Update w/ qr code doc link 2025-03-22 03:16:53 -07:00
billz
3e54b1d7bb Remove authentication sidebar item (redundant) 2025-03-22 03:14:11 -07:00
billz
51a0ce220c Update w/ disableValidation() to support logout btn 2025-03-22 03:12:46 -07:00
billz
05e20e3bab Handle logout action 2025-03-22 03:12:09 -07:00
billz
03fc2c42ad Update w/ logout() method 2025-03-22 03:11:47 -07:00
billz
401172eb36 Initial commit 2025-03-22 03:11:03 -07:00
Bill Zimmerman
71f1132bc8 Update plugin_helper.sh 2025-03-20 18:50:02 +01:00
Bill Zimmerman
605486feda Update w/ strict check on config destination path 2025-03-20 18:08:29 +01:00
Bill Zimmerman
23597e800d Fix: Revert set permissions on $destination 2025-03-20 12:05:30 +01:00
Bill Zimmerman
3218d87b1b Update dashboard screenshot 2025-03-20 11:13:32 +01:00
billz
0c58a6c92c Update release version 2025-03-20 03:11:31 -07:00
billz
afa3006de2 Minor: code cleanup 2025-03-20 02:54:30 -07:00
Bill Zimmerman
03b9bf9e7a Merge pull request #1793 from RaspAP/maint/php-warnings
Resolves numerous PHP warnings
2025-03-20 10:35:58 +01:00
billz
028c0d3e06 Move template handling of wg_log to include/wireguard 2025-03-20 02:12:47 -07:00
billz
0005488884 Fix: Use null coalescing operator on $_POST object, fetch wg_log, set $peer_id 2025-03-20 02:12:00 -07:00
billz
f73f25708c Fix: Cleanup, resolve warnings in getWifiInterface() 2025-03-20 02:10:40 -07:00
billz
4e411aaa6b Fix: Use null coalescing operator on user_id 2025-03-20 02:09:39 -07:00
billz
6adeab7586 Fix: Evaluate HTTP_ACCEPT_LANGUAGE, check ['locale'] 2025-03-20 02:08:54 -07:00
billz
16f6b7f979 Fix numerous php warnings for undefined vars 2025-03-20 02:07:51 -07:00
billz
068f363f09 Remove obsolete toggleState functions, add file_exists() check 2025-03-20 02:06:11 -07:00
billz
d4554c6429 Update w/ getBridgedState() global function 2025-03-20 02:05:11 -07:00
billz
a3caa6485c Fix: Use null coalescing operator to prevent warnings 2025-03-20 02:04:40 -07:00
billz
795d55a2cd Fix: Sanitize $peer_conf before output to header 2025-03-20 02:04:00 -07:00
billz
c53c1a27a4 Fix: Specify global Exception class by adding backslash 2025-03-20 02:02:35 -07:00
Bill Zimmerman
903cc6bd8e Merge pull request #1787 from RaspAP/maint/plugin-installer-keys
Updates PluginInstaller to handle signed third-party packages
2025-03-20 08:11:08 +01:00
Bill Zimmerman
13929acbd1 Merge pull request #1775 from RaspAP/fix/plugin-helper-config
Set permissions on parent directory when handling config files
2025-03-20 08:04:40 +01:00
Bill Zimmerman
f6da130fce Merge pull request #1792 from RaspAP/fix/installer-submodules
Updates plugin submodules after cloning parent repo
2025-03-20 08:01:10 +01:00
billz
1fc793c3fd Exec git submodule update --remote plugins on $source_dir 2025-03-19 12:00:07 -07:00
Bill Zimmerman
4b6ac1a415 Merge pull request #1748 from RaspAP/feat/dashboard-redesign
Dashboard redesign
2025-03-19 17:02:48 +01:00
billz
304010db40 Update client lease ouput w/ human-readable timestamps 2025-03-18 12:28:06 -07:00
billz
66563c9d95 Update hrefs w/ tab param to load specific tabs on page load 2025-03-18 12:11:50 -07:00
billz
5eca4c045b Add event listener to activate tab on page load 2025-03-18 12:10:47 -07:00
billz
fd953e7a71 Remove obsolete svg file (replaced by device.php) 2025-03-18 08:56:32 -07:00
billz
52b20cb491 Revise getEthernetClients() w/ more robust method 2025-03-18 08:55:35 -07:00
billz
c987d2800d Fix php warning: define $firewallManaged 2025-03-18 08:54:47 -07:00
billz
6d6bacd6a1 Update en_US locale msgs + compile .mo 2025-03-18 04:12:34 -07:00
billz
f0a0c9228f Replace static labels w/ gettext() for locale support 2025-03-18 04:11:47 -07:00
billz
dfef9e5233 Define firewallManaged when Firewall plugin is installed 2025-03-18 03:23:25 -07:00
billz
ab77af9e5d Update firewallEnabled() config check 2025-03-18 03:22:03 -07:00
billz
5ce06c6214 Revise dashed svg stroke width, color 2025-03-18 03:20:46 -07:00
billz
d5cc80d1f2 Update en_US locale w/ new dashboard msgs + compile .mo 2025-03-18 02:15:09 -07:00
billz
e3f04192b8 Replace static labels w/ gettext() for locale support 2025-03-18 02:14:02 -07:00
billz
cfb8435373 Revise formatClientLabel() w/ ngettext 2025-03-18 02:13:14 -07:00
billz
615f2abed1 Minor: formatting 2025-03-18 01:45:56 -07:00
billz
ac926e84d7 Style tweak for fa-stack status-item 2025-03-18 01:45:38 -07:00
billz
8846b96905 Update function mappings for absolute paths 2025-03-18 01:45:10 -07:00
billz
677e99ffd9 Update w/ relevant href links, indicate active/inactive states 2025-03-18 00:57:04 -07:00
billz
a82b30179b Add device, clients active/inactive states 2025-03-18 00:55:56 -07:00
billz
ae4e9be739 Change default SSID to RaspAP (finally) 2025-03-18 00:53:59 -07:00
billz
7091a4016a Update media query to reflect markup changes, add .inactive 2025-03-18 00:53:29 -07:00
billz
24b292bf33 Replace static color values w/ --raspap-* vars 2025-03-17 11:42:20 -07:00
billz
c00f006cdd Migrate inline functions to class methods in RaspAP\UI 2025-03-17 11:41:22 -07:00
billz
ed47a41c9c Initial commit 2025-03-17 11:38:17 -07:00
billz
39cc92853a Set z-index for solid + dashed-lines 2025-03-17 10:32:32 -07:00
billz
34f7563bed Replace static placeholder with renderClientConnections 2025-03-17 10:25:04 -07:00
billz
964d7b38a8 Create getConnectionIcon(), renderClientConnections() 2025-03-17 10:24:32 -07:00
billz
17fbbca046 Create renderConnection(), update template 2025-03-17 07:33:37 -07:00
billz
64faf296a6 Apply dynamic theme color w/ rendered svg lines 2025-03-17 07:32:24 -07:00
billz
f1c404a443 Rename file to device.php 2025-03-17 07:09:10 -07:00
billz
5ed1312406 Revise getEthernetClients() w/ more robust method comparing arp + dhcp 2025-03-17 07:08:29 -07:00
billz
00c18451cc Replace static svg w/ device.php 2025-03-17 07:07:25 -07:00
billz
b737b5c748 Use dynamic theme color w/ device svg 2025-03-17 07:06:21 -07:00
billz
d852990314 Replace var raspap-brand-color w/ raspap-theme-color 2025-03-17 07:05:03 -07:00
billz
182073f41e Update placeholder values w/ template data + active route states 2025-03-17 06:04:02 -07:00
billz
64d3a11866 Create getConnectionType(), getWirelessClients() getEthernetClients() 2025-03-17 06:02:48 -07:00
billz
4865e85655 Various minor tweaks 2025-03-17 04:07:27 -07:00
billz
3d6095d652 Define buttons w/ ob_start(), network details block + wifi bands 2025-03-17 04:07:05 -07:00
billz
bd0e379d01 Create getActiveVpnInterface(), update revision strings 2025-03-17 04:05:23 -07:00
billz
93395a8aa5 Refactor DisplayDashboard() 2025-03-17 04:04:27 -07:00
billz
339437f47f Set hostapd_led to warn when status is down 2025-03-17 03:28:32 -07:00
billz
80db7edf18 Compatibility fix for php 7.4 2025-03-16 12:38:22 -07:00
Bill Zimmerman
41e86a9e51 Merge pull request #1789 from RaspAP/fix/installer-update
Fix for automated version update on About page
2025-03-16 14:01:59 +01:00
billz
ef04678947 Get installed plugins, add status checks for services 2025-03-16 03:33:10 -07:00
billz
1a964a283f Replace static hex values w/ css var, style tweaks 2025-03-16 03:30:26 -07:00
billz
6c47375d18 Create adBlockStatus() 2025-03-16 03:29:19 -07:00
billz
c2abce1e35 Add -p flag to mkdir in _create_plugin_scripts() 2025-03-16 01:11:21 -07:00
billz
b4b715f6e4 Add --check 0 option for update 2025-03-16 01:10:26 -07:00
billz
8a7c954d88 Cleanup interface statistics code (obsolete) 2025-03-16 01:04:48 -07:00
billz
639f7605d1 Template tweaks, update labels w/ gettext 2025-03-16 01:01:23 -07:00
billz
fbcf9809c5 Update hotspot icon > fa-bullseye 2025-03-15 16:00:27 -07:00
billz
2474765820 Add device illustration to system basic tab 2025-03-15 15:18:24 -07:00
Bill Zimmerman
ba4507bd22 Merge branch 'master' into feat/dashboard-redesign 2025-03-15 19:54:34 +01:00
billz
2610c44ac9 Adjust stroke-width, remove rect w/ fill for dark mode support 2025-03-15 11:34:49 -07:00
billz
f608282aa5 Define --raspap-text-light, replace hex colors w/ vars, tweaks 2025-03-15 11:33:03 -07:00
billz
781f376bea Standardize revision descriptions 2025-03-15 11:31:59 -07:00
Bill Zimmerman
6e1c3b95c2 Revise dashed .svg files 2025-03-15 17:44:31 +01:00
billz
0331eb7b25 Minor: suppress debug output from key install 2025-03-15 00:59:36 -07:00
billz
c3a210907a Throw exception for invalid key structure, update messages 2025-03-15 00:58:53 -07:00
billz
3e0f1f16c1 Revise installRepositoryKeys() w/ array of key data 2025-03-14 11:37:47 -07:00
billz
c2be25271b Update w/ signed packages from manifest 2025-03-14 11:36:17 -07:00
billz
bb131a7f53 Revise key handling with parameters for url, keyring, repo + list 2025-03-14 11:35:30 -07:00
billz
c753865305 Added handling for GPG keys to enable third-party apt repos 2025-03-14 03:36:43 -07:00
billz
0bac7deccc Define helperScriptPath, create installRepositoryKeys() 2025-03-14 03:35:32 -07:00
Bill Zimmerman
9ada9f9e68 Merge pull request #1777 from RaspAP/maint/php-warnings
Fix PHP warning in getProviderValue()
2025-03-08 10:09:35 +00:00
billz
02b9e20ce3 Use ?? to avoid undefined key warnings 2025-03-02 00:24:57 -08:00
billz
3c8cf996b5 Set permissions on parent directory 2025-03-02 00:10:43 -08:00
Bill Zimmerman
313e2eb06f Update issue_form.yml 2025-03-01 08:18:54 +01:00
Bill Zimmerman
41fcbba5cc Update README.md 2025-02-23 14:30:10 +01:00
Bill Zimmerman
ce9916a792 Update README.md 2025-02-23 14:21:58 +01:00
Bill Zimmerman
3f830458b1 Update issue_form.yml 2025-02-23 09:32:02 +01:00
billz
a8e24b7629 Update release version 2025-02-21 09:07:12 -08:00
billz
80f5cb5f46 Set plugins submodule to track master 2025-02-21 05:25:59 -08:00
billz
d45aa752c6 Set plugins submodule to track master 2025-02-21 05:16:45 -08:00
billz
2a1e39a880 Update release version 2025-02-21 05:03:10 -08:00
Bill Zimmerman
8f402ab303 Merge pull request #1763 from RaspAP/maint/plugin-installer
PluginInstaller improvements + fixes
2025-02-21 09:16:29 +01:00
Bill Zimmerman
644d2fe6cb Update BACKERS.md 2025-02-21 08:02:56 +01:00
billz
b3fe781c19 Prepend this->rootPath if not absolute path 2025-02-19 06:03:07 -08:00
billz
69510a4e92 Set owner after copying dependencies 2025-02-19 06:00:32 -08:00
billz
163d727ee1 Fix: re-add copyPluginFiles() 2025-02-19 01:47:20 -08:00
billz
20fdd9024d Added safeOutputValue() 2025-02-19 01:25:07 -08:00
billz
23e0a6601a Fix: replace dirname var w/ destination param 2025-02-19 01:06:57 -08:00
billz
47d0305bec Revise installPlugin() to use plugins-available, fix copyJavaScriptFiles 2025-02-19 01:05:51 -08:00
billz
f28c5533bc Update pluginConfirm btn actions 2025-02-19 01:02:56 -08:00
billz
b89fe7c6b8 Set install_path, revise parameters used w/ installPlugin() 2025-02-19 01:02:04 -08:00
Bill Zimmerman
71ed223cf5 Merge pull request #1764 from juliovegap/master
Bug #1762
2025-02-19 08:47:52 +01:00
billz
335491124f Add install_path to ajax handler, various fixes + improvements 2025-02-18 09:00:49 -08:00
billz
e45c00b83c Create getRepository(), extend to installed & available plugins 2025-02-18 08:57:04 -08:00
billz
aa5fcef2d4 Add additional field to template, change source -> doc link 2025-02-18 08:55:52 -08:00
billz
01ddf6f4ea Update plugin details w/ javascript, default to none 2025-02-18 01:16:16 -08:00
juliovegap
bbac9a8802 Bug #1762
New versions of 'iw phy#0 info' provides frequencies output with decimals #1762. IW Parser patched to handle this new output format
2025-02-17 20:20:13 +01:00
billz
4374e78bbb Add JavaScript handling to installPlugin(), fix bug w/ manifest parsing 2025-02-17 02:35:42 -08:00
billz
6e0cf0b085 Update w/ javascript support 2025-02-17 02:34:20 -08:00
Bill Zimmerman
867a46bee9 Merge pull request #1757 from RaspAP/maint/version-motd
Fetch RaspAP version and set MOTD
2025-02-13 08:07:02 +01:00
Bill Zimmerman
4a89cb3b07 Update README.md 2025-02-13 07:59:38 +01:00
Bill Zimmerman
dc86f15b59 Update README.md 2025-02-12 23:45:26 +01:00
Bill Zimmerman
91c535ddbb Update README.md 2025-02-12 23:41:33 +01:00
Bill Zimmerman
f576b02858 Update README.md 2025-02-12 23:20:49 +01:00
billz
eb27ba2c66 Fetch RaspAP version and set MOTD 2025-02-12 08:30:33 -08:00
billz
362a08c00f Update release version 2025-02-09 00:54:27 -08:00
Bill Zimmerman
43ae90bd34 Merge pull request #1754 from RaspAP/maint/release-naming
Update naming to reflect clarity in arch, OS + purpose
2025-02-09 09:51:25 +01:00
billz
c34cbeca8d Exec raspi-config noint do_wifi_country 2025-02-09 00:46:27 -08:00
billz
33f66fdae5 Update naming to reflect clarity in arch, OS + purpose 2025-02-08 10:52:26 -08:00
Bill Zimmerman
cc9720e8ef Merge pull request #1752 from RaspAP/maint/installer-option
Adds -k --check option to install loader
2025-02-08 13:02:34 +01:00
Bill Zimmerman
74b1b10e31 Merge pull request #1753 from RaspAP/maint/release-check-flag
Specify --check 0 to skip connectivity check
2025-02-08 13:02:01 +01:00
billz
41ce72775e Set default repo at top of _main() 2025-02-08 01:41:43 -08:00
billz
9876ab1e3e Specify --check 0 to skip connectivity check 2025-02-08 01:22:52 -08:00
billz
bd0b226f61 Adds -k --check option to install loader 2025-02-08 01:18:47 -08:00
Bill Zimmerman
58501a74a7 Merge pull request #1750 from neo773/bs-redeisgn
feat: Dashboard redesign
2025-02-07 10:29:37 +01:00
neo773
80c1a04797 feat: dashboard redesign 2025-02-07 13:41:23 +05:30
billz
af3abe66f4 Initial commit 2025-02-06 02:44:37 -08:00
185 changed files with 19319 additions and 4230 deletions

View File

@@ -32,17 +32,19 @@ body:
options:
- label: I have read and understand the [issue reporting policy](https://docs.raspap.com/issues/).
required: true
- label: I have read and followed the [common sense checklist](https://docs.raspap.com/troubleshooting/#dos-and-donts).
required: true
- label: I observed this bug on a clean install of a [supported OS](https://docs.raspap.com/#compatible-operating-systems).
required: true
- label: I have followed the [project prerequisites](https://docs.raspap.com/#quick-start).
- label: I have followed the [project prerequisites](https://docs.raspap.com/quick_start/#quick-install).
required: true
- label: I have searched this repository for existing issues.
required: true
- label: I checked the [FAQ](https://docs.raspap.com/faq/) and [official documentation](https://docs.raspap.com/).
required: true
- label: I am using an [external wireless adapter](https://docs.raspap.com/issues/#external-hardware).
required: true
- label: I have generated a [RaspAP debug log](https://docs.raspap.com/ap-basics/#debug-log) and performed a [self-diagnosis](https://docs.raspap.com/ap-basics/#diagnosing-problems).
required: false
- label: I have generated a [RaspAP debug log](https://docs.raspap.com/troubleshooting/#debug-log) and performed a [self-diagnosis](https://docs.raspap.com/troubleshooting/#diagnosing-problems).
required: true
- type: dropdown
@@ -56,15 +58,15 @@ body:
- Raspberry Pi OS (64-bit) Lite Bullseye
- Raspberry Pi OS (32-bit) Lite Bullseye
- Armbian 23.05 (Suni)
- Debian Bookworm
- Ubuntu Server 23.04 (Lunar)
- Debian Bookworm
validations:
required: true
- type: dropdown
id: install
attributes:
label: Quick install or Manual setup?
label: Installation method
options:
- Pre-built image
- Quick install
- Manual setup
validations:
@@ -90,6 +92,7 @@ body:
- Raspberry Pi 3 Model B
- Raspberry Pi Zero 2 W
- Raspberry Pi Zero W
- Raspberry Pi Compute Module
- Orange Pi family
- Other
validations:
@@ -99,8 +102,8 @@ body:
attributes:
label: RaspAP version
options:
- 3.2.5 (Latest)
- Other
- Latest
- Other (specify below)
validations:
required: true
- type: dropdown
@@ -113,7 +116,6 @@ body:
- Not sure
validations:
required: true
- type: input
id: contact
attributes:

63
.github/scripts/add-raspap-stage.sh vendored Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/bash
set -e
mkdir -p stage-raspap/package-raspap
PRERUN_COMMANDS=""
INSTALLER_COMMANDS=""
if [ -n "$INSIDERS_USER" ] && [ -n "$INSIDERS_TOKEN" ]; then
echo ">>> Configuring for private Insiders build."
PRERUN_COMMANDS=$(
cat <<-EOF
echo "${INSIDERS_TOKEN}" > "\${ROOTFS_DIR}/etc/insiders_token"
chmod 600 "\${ROOTFS_DIR}/etc/insiders_token"
EOF
)
INSTALLER_COMMANDS=$(
cat <<-EOF
INSIDERS_TOKEN_VALUE=\$(cat /etc/insiders_token)
curl -sL https://install.raspap.com | bash -s -- \\
--yes --insiders --openvpn 1 --restapi 1 --adblock 1 --wireguard 1 --tcp-bbr 1 --check 0 \\
--name "${INSIDERS_USER}" --token "\$INSIDERS_TOKEN_VALUE"
rm -f /etc/insiders_token
EOF
)
else
echo ">>> Configuring for public build."
INSTALLER_COMMANDS=$(
cat <<-EOF
curl -sL https://install.raspap.com | bash -s -- --yes --openvpn 1 --restapi 1 --adblock 1 --wireguard 1 --tcp-bbr 1 --check 0
EOF
)
fi
cat >stage-raspap/prerun.sh <<-EOF
#!/bin/bash -e
if [ ! -d "\${ROOTFS_DIR}" ]; then
copy_previous
fi
${PRERUN_COMMANDS}
EOF
cat >stage-raspap/package-raspap/00-run-chroot.sh <<-EOF
#!/bin/bash -e
apt-get update -y && apt-get install -y curl dhcpcd5 iptables procps
${INSTALLER_COMMANDS}
# Set Wi-Fi country to prevent RF kill
raspi-config nonint do_wifi_country "US"
# Fetch RaspAP version and set MOTD
RASPAP_VERSION=\$(curl -sL https://install.raspap.com | bash -s -- --version)
echo "\$RASPAP_VERSION" | tee /etc/motd
EOF
chmod +x stage-raspap/prerun.sh
chmod +x stage-raspap/package-raspap/00-run-chroot.sh
echo "Build configuration complete."

View File

@@ -23,31 +23,13 @@ jobs:
uses: actions/checkout@v4
- name: Add RaspAP Stage
run: |
mkdir -p stage-raspap/package-raspap &&
{
cat > stage-raspap/package-raspap/00-run-chroot.sh <<-EOF
#!/bin/bash
apt-get update -y && apt-get install -y curl dhcpcd5 iptables procps
curl -sL https://install.raspap.com | bash -s -- --yes --openvpn 1 --restapi 1 --adblock 1 --wireguard 1 --tcp-bbr 1 --check 0
EOF
} &&
chmod +x stage-raspap/package-raspap/00-run-chroot.sh &&
{
cat > stage-raspap/prerun.sh <<-EOF
#!/bin/bash -e
if [ ! -d "\${ROOTFS_DIR}" ]; then
copy_previous
fi
EOF
} &&
chmod +x stage-raspap/prerun.sh
run: sh ./.github/scripts/add-raspap-stage.sh
- name: Build RaspAP Image
id: build
uses: usimd/pi-gen-action@v1
with:
image-name: "raspap-${{ github.ref_name }}-${{ matrix.arch }}"
image-name: "raspap-bookworm-${{ matrix.arch == '32-bit' && 'armhf' || 'arm64' }}-lite-${{ github.event.inputs.tag || github.ref_name }}"
enable-ssh: 1
stage-list: stage0 stage1 stage2 ./stage-raspap
verbose-output: true
@@ -57,8 +39,20 @@ jobs:
- name: Upload Artifact
uses: svenstaro/upload-release-action@v2
with:
asset_name: raspap-image-${{ github.ref_name }}-${{ matrix.arch }}.zip
asset_name: "raspap-bookworm-${{ matrix.arch == '32-bit' && 'armhf' || 'arm64' }}-lite-${{ github.event.inputs.tag || github.ref_name }}.img.zip"
file: ${{ steps.build.outputs.image-path }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
tag: ${{ github.event.inputs.tag || github.ref }}
overwrite: true
torrent:
needs: build-raspap-image
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Generate torrents for release
uses: devopsx/action-torrent@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
local: false

3
.gitignore vendored
View File

@@ -4,6 +4,7 @@ yarn-error.log
*.swp
includes/config.php
rootCA.pem
vendor
.env
locale/**/*.mo
app/net_activity
app/js/plugins/

5
.gitmodules vendored
View File

@@ -1,3 +1,4 @@
[submodule "plugins"]
path = plugins
url = https://github.com/RaspAP/plugins
path = plugins
url = https://github.com/RaspAP/plugins
branch = master

View File

@@ -1,4 +1,4 @@
<img width="465" alt="Insiders logo" src="https://user-images.githubusercontent.com/229399/115766971-e19e1900-a3a8-11eb-8c6f-379deb4313d2.png">
<img width="465" alt="Insiders logo" src="https://i.imgur.com/62TMUy5.png">
Development of RaspAP is made possible thanks to a sponsorware release model. This means that new features are first exclusively released to sponsors as part of **Insiders**. Read on to learn how sponsorship works, and how easy it is to get access to Insiders.
@@ -13,38 +13,47 @@ You can become a sponsor using your individual or organization's GitHub account.
**Important**: If you're sponsoring [RaspAP](https://github.com/RaspAP/sponsors) through a GitHub organization, please send a short email to [sponsors@raspap.com](mailto:sponsors@raspap.com) with the name of your organization and the account that should be added as a collaborator.
## Exclusive features
The following features are currently available exclusively to sponsors. A tangible side benefit of sponsorship is that Insiders are able to help steer future development of RaspAP. This is done through Insiders' access to discussions, feature requests, issues and pull requests in the private GitHub repository.
The following features are currently available exclusively to sponsors. A tangible side benefit of sponsorship is that Insiders are able to help steer future development of RaspAP. This is done through your Insiders access to discussions, feature requests, issues and pull requests in the private GitHub repository.
✅ [Network device management](https://docs.raspap.com/net-devices/)
✅ [Firewall settings](https://docs.raspap.com/firewall/)
✅ [WPA3-Personal AP security](https://docs.raspap.com/ap-basics/#wpa3-personal)
✅ [802.11w Protected Management Frames](https://docs.raspap.com/ap-basics/#80211w)
✅ [Printable Wi-Fi signs](https://docs.raspap.com/ap-basics/#printable-signs)
✅ [Drag & drop dashboard widgets](https://docs.raspap.com/ap-basics/#drag-drop-widgets)
✅ [MAC address cloning](https://docs.raspap.com/net-devices/#changing-the-mac-address)
✅ [Network diagnostics](https://docs.raspap.com/net-devices/#diagnostics)
✅ [WireGuard VPN kill switch](https://docs.raspap.com/wireguard/#kill-switch)
✅ [Dynamic DNS support](https://docs.raspap.com/dynamicdns/)
✅ [Multiple WireGuard configs](https://docs.raspap.com/wireguard/#multiple-configs)
✅ [Wireless LAN routing](https://docs.raspap.com/wlanrouting/)
✅ [Custom user avatars](https://docs.raspap.com/authentication/#custom-user-avatars)
✅ [WiFi repeater mode](https://docs.raspap.com/ap-basics/#wifi-repeater-mode)
✅ [NTP Service](https://docs.raspap.com/ntp/)
✅ [Limited privilege user role](https://docs.raspap.com/authentication/#limited-privilege-user-role)
✅ [Limited privilege user role](https://docs.raspap.com/authentication/#limited-privilege-user-role)
✅ [Tailscale VPN](https://docs.raspap.com/tailscale/)
✅ [Inspect network adapters](https://docs.raspap.com/troubleshooting/#inspect-network-adapters)
Look for the list above to grow as we add more exclusive features. Be sure to visit this page from time to time to learn about what's new, check the [Insiders docs page](https://docs.raspap.com/insiders/) and follow [@RaspAP on Twitter](https://twitter.com/rasp_ap) to stay updated.
Look for the list above to grow as we add more exlcusive features. Have an idea or suggestion for a future enhancement? Start or join an [Insiders discussion](https://github.com/RaspAP/raspap-insiders/discussions) and let us know!
## Funding targets
Below is a list of funding targets. When a funding target is reached, the features that are tied to it are merged back into RaspAP and released to the public for general availability.
### $1000
The second **Insiders Edition** includes the features listed above.
### $1,500 - 3rd Insiders Edition
The **3rd Insiders Edition** includes the exclusive features listed above.
### $500
The [first Insiders Edition goal](https://docs.raspap.com/insiders/#500-1st-insiders-edition) was reached in December 2021. Thank you sponsors!
### $500 - 1st Insiders Edition (completed)
✅ Multiple OpenVPN client configs
✅ OpenVPN certificate authentication
✅ OpenVPN service logging
✅ Night mode toggle
✅ Restrict network to static clients
✅ WireGuard support
✅ Set AP transmit power
### $1,000 - 2nd Insiders Edition (completed)
✅ Firewall settings
✅ WPA3-Personal AP security
✅ 802.11w Protected Management Frames
✅ Printable Wi-Fi signs
✅ Network diagnostics
✅ Dynamic DNS
✅ WireGuard kill switch
✅ NTP Service
## Quarterly giving
Beginning in 2022, each quarter 15% of all proceeds from Insiders will be donated directly to the [Raspberry Pi Foundation](https://www.raspberrypi.org/). The Raspberry Pi Foundation is a UK-based charity that works to put the power of computing and digital making into the hands of people all over the world.
Each quarter, 15% of all proceeds from Insiders are [donated directly to the Raspberry Pi Foundation](https://docs.raspap.com/insiders/#quarterly-giving). The Raspberry Pi Foundation is a UK-based charity that works to put the power of computing and digital making into the hands of people all over the world.
[![Get involved with the Raspberry Pi Foundation](https://img.youtube.com/vi/dEzg92g1LHw/0.jpg)](https://www.youtube.com/watch?v=dEzg92g1LHw)

View File

@@ -16,7 +16,7 @@ RaspAP is made possible by a strong [community of developers](https://github.com
* [GitHub discussions](https://github.com/RaspAP/raspap-webgui/discussions)
* [Discord chat](https://discord.gg/KVAsaAR)
* [Twitter](https://twitter.com/rasp_ap)
* [X](https://x.com/rasp_ap)
* [Reddit](https://www.reddit.com/r/RaspAP/)
If you enjoy using RaspAP and would like to support our work financially, consider becoming an [Insider](https://github.com/sponsors/RaspAP).

View File

@@ -1,14 +1,13 @@
![](https://i.imgur.com/xeKD93p.png)
[![Release 3.2.6](https://img.shields.io/badge/release-v3.2.6-green)](https://github.com/raspap/raspap-webgui/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Join Insiders](https://img.shields.io/static/v1?label=Insiders&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/RaspAP) [![Build Status](https://app.travis-ci.com/RaspAP/raspap-webgui.svg?branch=master)](https://app.travis-ci.com/RaspAP/raspap-webgui) [![Crowdin](https://badges.crowdin.net/raspap/localized.svg)](https://crowdin.com/project/raspap) [![Twitter URL](https://img.shields.io/twitter/url?label=%40RaspAP&logoColor=%23d8224c&url=https%3A%2F%2Ftwitter.com%2Frasp_ap)](https://twitter.com/rasp_ap) [![Reddit](https://img.shields.io/badge/%2Fr%2FRaspAP-e05d44?style=flat&logo=Reddit&logoColor=white&labelColor=e05d44&color=b14835)](https://reddit.com/r/RaspAP) [![Discord](https://img.shields.io/discord/642436993451819018?color=7289DA&label=Discord&logo=discord&style=flat)](https://discord.gg/KVAsaAR)
![RaspAP Custom OS images](https://github.com/user-attachments/assets/e871adf1-123c-450b-94eb-80a185c242cc)
[![Release 3.4.0](https://img.shields.io/badge/release-v3.4.0-green)](https://github.com/raspap/raspap-webgui/releases) [![Awesome](https://awesome.re/badge.svg)](https://github.com/thibmaek/awesome-raspberry-pi) [![Join Insiders](https://img.shields.io/static/v1?label=Insiders&message=%E2%9D%A4&logo=GitHub&color=ff69b4)](https://github.com/sponsors/RaspAP) [![Build Status](https://app.travis-ci.com/RaspAP/raspap-webgui.svg?branch=master)](https://app.travis-ci.com/RaspAP/raspap-webgui) [![Crowdin](https://badges.crowdin.net/raspap/localized.svg)](https://crowdin.com/project/raspap) [![Twitter URL](https://img.shields.io/twitter/url?label=%40RaspAP&logoColor=%23d8224c&url=https%3A%2F%2Ftwitter.com%2Frasp_ap)](https://twitter.com/rasp_ap) [![Reddit](https://img.shields.io/badge/%2Fr%2FRaspAP-e05d44?style=flat&logo=Reddit&logoColor=white&labelColor=e05d44&color=b14835)](https://reddit.com/r/RaspAP) [![Discord](https://img.shields.io/discord/642436993451819018?color=7289DA&label=Discord&logo=discord&style=flat)](https://discord.gg/KVAsaAR)
RaspAP is feature-rich wireless router software that _just works_ on many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our [custom OS images](#pre-built-image), [Quick installer](#quick-installer) and [Docker container](#docker-support) create a known-good default configuration for all current Raspberry Pis with onboard wireless. A fully responsive, mobile-ready interface gives you control over the relevant services and networking options. Advanced DHCP settings, [WireGuard](https://docs.raspap.com/wireguard/), [Tailscale](https://docs.raspap.com/tailscale/) and [OpenVPN](https://docs.raspap.com/openvpn/) support, [SSL certificates](https://docs.raspap.com/ssl/), [ad blocking](#ad-blocking), security audits, [captive portal integration](https://docs.raspap.com/captive/), themes and [multilingual options](https://docs.raspap.com/translations/) are included.
RaspAP is feature-rich wireless router software that _just works_ on many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our popular [Quick installer](#quick-installer) and [Docker container](#docker-support) create a known-good default configuration for all current Raspberry Pis with onboard wireless. A fully responsive, mobile-ready interface gives you control over the relevant services and networking options. Advanced DHCP settings, WireGuard and OpenVPN support, [SSL certificates](https://docs.raspap.com/ssl/), security audits, [captive portal integration](https://docs.raspap.com/captive/), themes and [multilingual options](https://docs.raspap.com/translations/) are included.
RaspAP has been featured on sites such as [Instructables](http://www.instructables.com/id/Raspberry-Pi-As-Completely-Wireless-Router/), [Adafruit](https://blog.adafruit.com/2016/06/24/raspap-wifi-configuration-portal-piday-raspberrypi-raspberry_pi/), [Raspberry Pi Weekly](https://www.raspberrypi.org/weekly/commander/) and [Awesome Raspberry Pi](https://project-awesome.org/thibmaek/awesome-raspberry-pi) and implemented in countless projects.
RaspAP has been featured by [PC World](https://www.pcwelt.de/article/1789512/raspberry-pi-als-wlan-router.html), [MSN](https://www.msn.com/en-us/news/technology/4-reasons-i-installed-raspap-on-my-raspberry-pi/ar-AA1GLHdE), [Adafruit](https://blog.adafruit.com/2016/06/24/raspap-wifi-configuration-portal-piday-raspberrypi-raspberry_pi/), [Raspberry Pi Weekly](https://www.raspberrypi.org/weekly/commander/), and [Awesome Raspberry Pi](https://project-awesome.org/thibmaek/awesome-raspberry-pi) and implemented in [countless projects](https://github.com/RaspAP/raspap-awesome#projects).
We hope you enjoy using RaspAP as much as we do creating it. Tell us how you use this with [your own projects](https://github.com/raspap/raspap-awesome).
![](https://github.com/user-attachments/assets/c9bb0386-0b10-48cc-8b42-0a587a84fc37)
![dashboard](https://github.com/user-attachments/assets/f7cf5c32-4d95-4ac8-8a30-6d892d7ac6ed)
<img width="32.5%" alt="Wifi Client" src="https://github.com/user-attachments/assets/95696ddc-da84-4339-97cc-f2a173054664">
<img width="32.5%" alt="Hotspot" src="https://github.com/user-attachments/assets/c1c4de15-3ff2-4d3c-a7af-339c24896749">
<img width="32.5%" alt="Adblock" src="https://github.com/user-attachments/assets/ab925687-8407-4bec-a952-9dc6a2675f49">
@@ -18,15 +17,13 @@ We hope you enjoy using RaspAP as much as we do creating it. Tell us how you use
## Contents
- [Prerequisites](#prerequisites)
- [Quick installer](#quick-installer)
- [Quick start](#quick-start)
- [Join Insiders](#join-insiders)
- [WireGuard support](#wireguard-support)
- [OpenVPN support](#openvpn-support)
- [VPN Provider support](#vpn-provider-support)
- [Ad Blocking](#ad-blocking)
- [Bridged AP](#bridged-ap)
- [Simultaneous AP and Wifi client](#simultaneous-ap-and-wifi-client)
- [Manual installation](#manual-installation)
- [802.11ac 5GHz support](#80211ac-5ghz-support)
- [Supported operating systems](#supported-operating-systems)
@@ -38,30 +35,43 @@ We hope you enjoy using RaspAP as much as we do creating it. Tell us how you use
- [Reporting issues](#reporting-issues)
- [License](#license)
## Prerequisites
Start with a clean install of the [latest release of Raspberry Pi OS Lite](https://www.raspberrypi.com/software/operating-systems/). Both the 32- and 64-bit Lite versions are supported. The Raspberry Pi OS desktop distro is [unsupported](https://docs.raspap.com/faq/#distros).
## Quick start
RaspAP gives you two different ways to get up and running quickly. The simplest and recommended approach is to use a custom Raspberry Pi OS image with RaspAP preinstalled. This option eliminates guesswork and gives you a base upon which to build. Alternatively, you may execute the Quick installer on an existing [compatible OS](https://docs.raspap.com/#compatible-operating-systems).
### Pre-built image
Custom Raspberry Pi OS Lite images with the latest RaspAP are available for [direct download](https://github.com/RaspAP/raspap-webgui/releases/latest). This includes both 32- and 64-bit builds for ARM architectures.
| Operating system | Debian version | Kernel version | RaspAP version | Size |
| ---------------------| ---------------|-----------------|----------------|-------|
| Raspberry Pi OS (64-bit) Lite | 12 (bookworm) | 6.6 | Latest | 777 MB|
| Raspberry Pi OS (32-bit) Lite | 12 (bookworm) | 6.6 | Latest | 805 MB|
These images are automatically generated with each release of RaspAP. You may choose between an `arm64` or `armhf` (32-bit) based build. Refer to [this resource](https://www.raspberrypi.com/software/operating-systems/) to ensure compatibility with your hardware.
After downloading your desired image from the [latest release page](https://github.com/RaspAP/raspap-webgui/releases/latest), use a utility such as the Raspberry Pi Imager or [balenaEtcher](https://www.balena.io/etcher) to flash the OS image onto a microSD card. Insert the card into your device and boot it up. The latest RaspAP release version with the most popular optional components will be active and ready for you to configure.
### Quick installer
Alternatively, start with a clean install of a [latest release of Raspberry Pi OS](https://www.raspberrypi.org/software/operating-systems/). Both the 32- and 64-bit release versions are supported, as well as the latest 64-bit Desktop distribution.
Update RPi OS to its latest version, including the kernel and firmware, followed by a reboot:
1. Update Raspbian, including the kernel and firmware, followed by a reboot:
```
sudo apt-get update
sudo apt-get full-upgrade
sudo reboot
```
2. Set the "WLAN country" option in `raspi-config`'s **Localisation Options**: `sudo raspi-config`
Set the WiFi country in raspi-config's **Localisation Options**: `sudo raspi-config`.
3. If you have a device without an onboard wireless chipset, the [**Edimax Wireless 802.11b/g/n nano USB adapter**](https://www.edimax.com/edimax/merchandise/merchandise_detail/data/edimax/global/wireless_adapters_n150/ew-7811un) is an excellent option it's small, cheap and has good driver support.
With the prerequisites done, you can proceed with either the Quick installer or Manual installation steps below.
## Quick installer
Install RaspAP from your device's shell prompt:
```sh
curl -sL https://install.raspap.com | bash
```
The [installer](https://docs.raspap.com/quick/) will complete the steps in the manual installation (below) for you.
After the reboot at the end of the installation the wireless network will be
configured as an access point as follows:
The Quick installer will respond to several [command line arguments](https://docs.raspap.com/quick/), or switches, to customize your installation in a variety of ways, or install one of RaspAP's optional helper tools.
### Initial settings
After completing either of these setup options, the wireless AP network will be configured as follows:
* IP address: 10.3.141.1
* Username: admin
* Password: secret
@@ -69,7 +79,7 @@ configured as an access point as follows:
* SSID: `raspi-webgui`
* Password: ChangeMe
**Note:** As the name suggests, the Quick Installer is a great way to quickly setup a new AP. However, it does not automagically detect the unique configuration of your system. Best results are obtained by connecting to ethernet (`eth0`) or as a WiFi client, also known as managed mode, with `wlan0`. For the latter, refer to [this FAQ](https://docs.raspap.com/faq/#headless). Special instructions for the Pi Zero W are [available here](https://docs.raspap.com/ap-sta/).
It's _strongly recommended_ that your first post-install action is to change the default admin [authentication](https://docs.raspap.com/authentication/) settings. Thereafter, your AP's [basic settings](https://docs.raspap.com/ap-basics/) and many [advanced options](https://docs.raspap.com/ap-basics#advanced-options) are now ready to be modified by RaspAP.
Please [read this](https://docs.raspap.com/issues/) before reporting an issue.
@@ -118,11 +128,6 @@ By default RaspAP configures a routed AP for your clients to connect to. A bridg
More information on Bridged AP mode is provided [in our documentation](https://docs.raspap.com/bridged/).
## Simultaneous AP and Wifi client
RaspAP lets you create an AP with a Wifi client configuration, often called [AP-STA mode](https://docs.raspap.com/ap-sta/). With your system configured in managed mode, enable the AP from the **Advanced** tab of **Configure hotspot** by sliding the **Wifi client AP mode** toggle. Save settings and start the hotspot. The managed mode AP is functional without restart.
**Note:** This option is disabled until you configure your system as a wireless client. For a device operating in [managed mode](https://docs.raspap.com/faq/#headless) without an `eth0` connection, this configuration must be enabled [_before_ a reboot](https://docs.raspap.com/ap-sta/).
## Manual installation
Detailed manual setup instructions are provided [on our documentation site](https://docs.raspap.com/manual/).
@@ -139,11 +144,10 @@ RaspAP was originally made for Raspbian, but now also installs on the following
| Raspberry Pi OS | (64-bit) Desktop Bookworm | ARM | Official |
| Raspberry Pi OS | (64-bit) Lite Bullseye | ARM | Official |
| Raspberry Pi OS | (32-bit) Lite Bullseye | ARM | Official |
| Armbian | 23.11 (Jammy) | [ARM](https://docs.armbian.com/#supported-socs) | Official |
| Armbian | 23.11 (Jammy) | [ARM](https://docs.armbian.com/#supported-socs) | Beta |
| Debian | Bookworm | ARM / x86_64 | Beta |
| Ubuntu | Server 23.04 (Lunar) | ARM / x86_64 | Beta |
<img src="https://github.com/RaspAP/raspap-webgui/assets/229399/6fe62f2d-631a-46c9-8ceb-83ebf0ade6a9" style="width:640px;" />
<img src="https://i.imgur.com/XiAJNKb.png" style="width:480px;" />
You are also encouraged to use RaspAP's community-led [Docker container](#docker-support). Please note that "supported" is not a guarantee. If you are able to improve support for your preferred distro, we encourage you to [actively contribute](#how-to-contribute) to the project.

View File

@@ -1,55 +1,61 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
define('BLOCKLISTS_FILE', __DIR__ . '/../../config/blocklists.json');
if (isset($_POST['blocklist_id'])) {
$blocklist_id = escapeshellcmd($_POST['blocklist_id']);
$blocklist_id = $_POST['blocklist_id'];
$json = file_get_contents(BLOCKLISTS_FILE);
$allLists = json_decode($json, true);
switch ($blocklist_id) {
case "StevenBlack/hosts \(default\)":
$list_url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
$dest_file = "hostnames.txt";
break;
case "badmojr/1Hosts \(Mini\)":
$list_url = "https://badmojr.github.io/1Hosts/mini/hosts.txt";
$dest_file = "hostnames.txt";
break;
case "badmojr/1Hosts \(Lite\)":
$list_url = "https://badmojr.github.io/1Hosts/Lite/hosts.txt";
$dest_file = "hostnames.txt";
break;
case "badmojr/1Hosts \(Pro\)":
$list_url = "https://badmojr.github.io/1Hosts/Pro/hosts.txt";
$dest_file = "hostnames.txt";
break;
case "badmojr/1Hosts \(Xtra\)":
$list_url = "https://badmojr.github.io/1Hosts/Xtra/hosts.txt";
$dest_file = "hostnames.txt";
break;
case "oisd/big \(default\)":
$list_url = "https://big.oisd.nl/dnsmasq";
$dest_file = "domains.txt";
break;
case "oisd/small":
$list_url = "https://small.oisd.nl/dnsmasq";
$dest_file = "domains.txt";
break;
case "oisd/nsfw":
$list_url = "https://nsfw.oisd.nl/dnsmasq";
$dest_file = "domains.txt";
break;
if ($allLists === null) {
echo json_encode([
'return' => 3,
'output' => ['Failed to parse blocklists.json']
]);
exit;
}
$blocklist = $list_url . $dest_file;
$dest = substr($dest_file, 0, strrpos($dest_file, "."));
$flatList = flattenList($allLists);
if (!isset($flatList[$blocklist_id])) {
echo json_encode(['return' => 1, 'output' => ['Invalid blocklist ID']]);
exit;
}
$list_url = escapeshellcmd($flatList[$blocklist_id]['list_url']);
$dest_file = escapeshellcmd($flatList[$blocklist_id]['dest_file']);
$dest = pathinfo($dest_file, PATHINFO_FILENAME);
$scriptPath = RASPI_CONFIG . '/adblock/update_blocklist.sh';
if (!file_exists($scriptPath)) {
echo json_encode([
'return' => 5,
'output' => ["Update script not found: $scriptPath"]
]);
exit;
}
exec("sudo $scriptPath $list_url $dest_file " . RASPI_ADBLOCK_LISTPATH, $output, $return_var);
echo json_encode([
'return' => $return_var,
'output' => $output,
'list' => $dest
]);
exec("sudo /etc/raspap/adblock/update_blocklist.sh $list_url $dest_file " .RASPI_ADBLOCK_LISTPATH, $return);
$jsonData = ['return'=>$return,'list'=>$dest];
echo json_encode($jsonData);
} else {
$jsonData = ['return'=>2,'output'=>['Error getting data']];
echo json_encode($jsonData);
echo json_encode(['return' => 2, 'output' => ['No blocklist ID provided']]);
}
function flattenList(array $grouped): array {
$flat = [];
foreach ($grouped as $group) {
foreach ($group as $name => $meta) {
$flat[$name] = $meta;
}
}
return $flat;
}

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
$interface = filter_input(INPUT_GET, 'inet', FILTER_SANITIZE_SPECIAL_CHARS);

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
if (filter_input(INPUT_GET, 'tu') == 'h') {

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/functions.php';

View File

@@ -1,37 +1,29 @@
<?php
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/config.php';
require_once '../../includes/session.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/session.php';
require_once '../../includes/functions.php';
if (isset($_POST['csrf_token'])) {
if (csrfValidateRequest() && !CSRFValidate()) {
handleInvalidCSRFToken();
}
$return = 0;
$path = "../../config";
$configs = array(
array("src" => $path .'/hostapd.conf', "tmp" => "/tmp/hostapddata", "dest" => RASPI_HOSTAPD_CONFIG),
array("src" => $path .'/dhcpcd.conf', "tmp" => "/tmp/dhcpddata", "dest" => RASPI_DHCPCD_CONFIG),
array("src" => $path .'/090_wlan0.conf', "tmp" => "/tmp/dnsmasqdata", "dest" => RASPI_DNSMASQ_PREFIX.'wlan0.conf'),
array("src" => $path .'/090_raspap.conf', "tmp" => "/tmp/dnsmasqdata", "dest" => RASPI_DNSMASQ_PREFIX.'raspap.conf'),
);
$return = 0;
$path = "../../config";
$configs = array(
array("src" => $path .'/hostapd.conf', "tmp" => "/tmp/hostapddata", "dest" => RASPI_HOSTAPD_CONFIG),
array("src" => $path .'/dhcpcd.conf', "tmp" => "/tmp/dhcpddata", "dest" => RASPI_DHCPCD_CONFIG),
array("src" => $path .'/090_wlan0.conf', "tmp" => "/tmp/dnsmasqdata", "dest" => RASPI_DNSMASQ_PREFIX.'wlan0.conf'),
array("src" => $path .'/090_raspap.conf', "tmp" => "/tmp/dnsmasqdata", "dest" => RASPI_DNSMASQ_PREFIX.'raspap.conf'),
);
foreach ($configs as $config) {
try {
$tmp = file_get_contents($config["src"]);
file_put_contents($config["tmp"], $tmp);
system("sudo cp ".$config["tmp"]. " ".$config["dest"]);
} catch (Exception $e) {
$return = $e->getCode();
}
foreach ($configs as $config) {
try {
$tmp = file_get_contents($config["src"]);
file_put_contents($config["tmp"], $tmp);
system("sudo cp ".$config["tmp"]. " ".$config["dest"]);
} catch (Exception $e) {
$return = $e->getCode();
}
$jsonData = ['return'=>$return];
echo json_encode($jsonData);
} else {
handleInvalidCSRFToken();
}
$jsonData = ['return'=>$return];
echo json_encode($jsonData);

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
exec("ls /sys/class/net | grep -v lo", $interfaces);

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
exec('cat '. RASPI_HOSTAPD_CONFIG, $hostapdconfig);
@@ -18,3 +17,4 @@ foreach ($hostapdconfig as $hostapdconfigline) {
};
$channel = intval($arrConfig['channel']);
echo json_encode($channel);

View File

@@ -1,10 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../src/RaspAP/Parsers/IwParser.php';
require_once '../../includes/authenticate.php';
if (isset($_POST['interface'])) {

View File

@@ -1,10 +1,9 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/functions.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
if (isset($_POST['interface'])) {

View File

@@ -1,68 +1,19 @@
<?php
require '../../includes/csrf.php';
use RaspAP\Networking\Hotspot\DhcpcdManager;
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/functions.php';
$dhcpcdManager = new DhcpcdManager();
$interface = $_POST['iface'];
if (isset($interface)) {
// fetch dnsmasq.conf settings for interface
exec('cat '. escapeshellarg(RASPI_DNSMASQ_PREFIX.$interface.'.conf'), $return);
$conf = ParseConfig($return);
$dhcpdata['DHCPEnabled'] = empty($conf) ? false : true;
if (is_string($conf['dhcp-range'])) {
$arrRange = explode(",", $conf['dhcp-range']);
} else {
$arrRange = explode(",", $conf['dhcp-range'][0]);
}
$dhcpdata['RangeStart'] = $arrRange[0] ?? null;
$dhcpdata['RangeEnd'] = $arrRange[1] ?? null;
$dhcpdata['RangeMask'] = $arrRange[2] ?? null;
$dhcpdata['leaseTime'] = $arrRange[3] ?? null;
$dhcpHost = $conf["dhcp-host"] ?? null;
$dhcpHost = empty($dhcpHost) ? [] : $dhcpHost;
$dhcpdata['dhcpHost'] = is_array($dhcpHost) ? $dhcpHost : [ $dhcpHost ];
$upstreamServers = is_array($conf['server'] ?? null) ? $conf['server'] : [ $conf['server'] ?? '' ];
$dhcpdata['upstreamServersEnabled'] = empty($conf['server']) ? false: true;
$dhcpdata['upstreamServers'] = array_filter($upstreamServers);
preg_match('/([0-9]*)([a-z])/i', $dhcpdata['leaseTime'], $arrRangeLeaseTime);
$dhcpdata['leaseTime'] = $arrRangeLeaseTime[1];
$dhcpdata['leaseTimeInterval'] = $arrRangeLeaseTime[2];
if (isset($conf['dhcp-option'])) {
$arrDns = explode(",", $conf['dhcp-option']);
if ($arrDns[0] == '6') {
if (count($arrDns) > 1) {
$dhcpdata['DNS1'] = $arrDns[1] ?? null;
}
if (count($arrDns) > 2) {
$dhcpdata['DNS2'] = $arrDns[2] ?? null;
}
}
}
// fetch dhcpcd.conf settings for interface
$conf = file_get_contents(RASPI_DHCPCD_CONFIG);
preg_match('/^#\sRaspAP\s'.$interface.'\s.*?(?=\s*+$)/ms', $conf, $matched);
preg_match('/metric\s(\d*)/', $matched[0], $metric);
preg_match('/static\sip_address=(.*)/', $matched[0], $static_ip);
preg_match('/static\srouters=(.*)/', $matched[0], $static_routers);
preg_match('/static\sdomain_name_server=(.*)/', $matched[0], $static_dns);
preg_match('/fallback\sstatic_'.$interface.'/', $matched[0], $fallback);
preg_match('/(?:no)?gateway/', $matched[0], $gateway);
preg_match('/nohook\swpa_supplicant/', $matched[0], $nohook_wpa_supplicant);
$dhcpdata['Metric'] = $metric[1] ?? null;
$dhcpdata['StaticIP'] = isset($static_ip[1]) && strpos($static_ip[1], '/') !== false
? substr($static_ip[1], 0, strpos($static_ip[1], '/'))
: ($static_ip[1] ?? '');
$dhcpdata['SubnetMask'] = cidr2mask($static_ip[1] ?? '');
$dhcpdata['StaticRouters'] = $static_routers[1] ?? null;
$dhcpdata['StaticDNS'] = $static_dns[1] ?? null;
$dhcpdata['FallbackEnabled'] = empty($fallback) ? false: true;
$dhcpdata['DefaultRoute'] = $gateway[0] == "gateway";
$dhcpdata['NoHookWPASupplicant'] = ($nohook_wpa_supplicant[0] ?? '') == "nohook wpa_supplicant";
$dhcpdata = $dhcpcdManager->getInterfaceConfig($interface);
echo json_encode($dhcpdata);
}

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/locale.php';

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
// fetch wg client.conf

View File

@@ -1,12 +1,11 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
$entity = escapeshellcmd($_POST['entity']);
$entity = escapeshellarg($_POST['entity']);
if (isset($entity)) {

View File

@@ -1,23 +1,26 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/defaults.php';
require_once '../../includes/functions.php';
require_once '../../includes/wifi_functions.php';
use RaspAP\Networking\Hotspot\WiFiManager;
$wifi = new WiFiManager();
$networks = [];
$network = null;
$ssid = null;
knownWifiStations($networks);
nearbyWifiStations($networks, !isset($_REQUEST["refresh"]));
connectedWifiStations($networks);
sortNetworksByRSSI($networks);
foreach ($networks as $ssid => $network) $networks[$ssid]["ssidutf8"] = ssid2utf8( $ssid );
$wifi->knownWifiStations($networks);
$wifi->nearbyWifiStations($networks, !isset($_REQUEST["refresh"]));
$wifi->connectedWifiStations($networks);
$wifi->sortNetworksByRSSI($networks);
foreach ($networks as $ssid => $network) $networks[$ssid]["ssidutf8"] = $wifi->ssid2utf8( $ssid );
$connected = array_filter($networks, function($n) { return $n['connected']; } );
$known = array_filter($networks, function($n) { return !$n['connected'] && $n['configured']; } );

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/functions.php';

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/functions.php';

View File

@@ -1,29 +1,26 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
require_once '../../src/RaspAP/Plugins/PluginInstaller.php';
$pluginInstaller = \RaspAP\Plugins\PluginInstaller::getInstance();
$plugin_uri = $_POST['plugin_uri'] ?? null;
$plugin_version = $_POST['plugin_version'] ?? null;
$install_path = $_POST['install_path'] ?? null;
if (isset($plugin_uri) && isset($plugin_version)) {
$archiveUrl = rtrim($plugin_uri, '/') . '/archive/refs/tags/' . $plugin_version .'.zip';
if (isset($plugin_uri, $plugin_version, $install_path)) {
try {
$return = $pluginInstaller->installPlugin($archiveUrl);
$return = $pluginInstaller->installPlugin($plugin_uri, $plugin_version, $install_path);
echo json_encode($return);
} catch (Exception $e) {
http_response_code(422); // Unprocessable Content
http_response_code(422); // unprocessable content
echo json_encode(['error' => $e->getMessage()]);
}
} else {
http_response_code(400); // Bad Request
echo json_encode(['error' => 'Plugin URI and version are required']);
echo json_encode(['error' => 'Plugin URI, version, and install path are required']);
exit;
}

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
$lastActivity = $_SESSION['lastActivity'] ?? time();

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
$action = escapeshellcmd($_POST['a']);

View File

@@ -1,27 +1,22 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../includes/authenticate.php';
require_once '../../includes/defaults.php';
require_once '../../includes/functions.php';
if (isset($_POST['csrf_token'])) {
if (csrfValidateRequest() && !CSRFValidate()) {
handleInvalidCSRFToken();
}
$uri = RASPI_API_ENDPOINT;
preg_match('/(\d+(\.\d+)+)/', RASPI_VERSION, $matches);
$thisRelease = $matches[0];
$uri = RASPI_API_ENDPOINT;
preg_match('/(\d+(\.\d+)+)/', RASPI_VERSION, $matches);
$thisRelease = $matches[0];
$json = shell_exec("wget --timeout=5 --tries=1 $uri -qO -");
$data = json_decode($json, true);
$tagName = $data['tag_name'];
$updateAvailable = checkReleaseVersion($thisRelease, $tagName);
$json = shell_exec("wget --timeout=5 --tries=1 $uri -qO -");
$data = json_decode($json, true);
$tagName = $data['tag_name'];
$updateAvailable = checkReleaseVersion($thisRelease, $tagName);
$response['tag'] = $tagName;
$response['update'] = $updateAvailable;
echo json_encode($response);
$response['tag'] = $tagName;
$response['update'] = $updateAvailable;
echo json_encode($response);
} else {
handleInvalidCSRFToken();
}

View File

@@ -1,25 +1,18 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
if (isset($_POST['csrf_token'])) {
if (csrfValidateRequest() && !CSRFValidate()) {
handleInvalidCSRFToken();
}
$root = getenv("DOCUMENT_ROOT");
exec('sudo '.RASPI_CONFIG.'/system/debuglog.sh -i '.$root, $return);
$root = getenv("DOCUMENT_ROOT");
exec('sudo '.RASPI_CONFIG.'/system/debuglog.sh -i '.$root, $return);
$logOutput = implode(PHP_EOL, $return);
$tempDir = sys_get_temp_dir();
$filePath = $tempDir . DIRECTORY_SEPARATOR . RASPI_DEBUG_LOG;
$handle = fopen($filePath, "w");
fwrite($handle, $logOutput);
fclose($handle);
echo json_encode($filePath);
$logOutput = implode(PHP_EOL, $return);
$tempDir = sys_get_temp_dir();
$filePath = $tempDir . DIRECTORY_SEPARATOR . RASPI_DEBUG_LOG;
$handle = fopen($filePath, "w");
fwrite($handle, $logOutput);
fclose($handle);
echo json_encode($filePath);
} else {
handleInvalidCSRFToken();
}

View File

@@ -1,9 +1,8 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
$tempDir = sys_get_temp_dir();

View File

@@ -1,24 +1,16 @@
<?php
require '../../includes/csrf.php';
require_once '../../includes/autoload.php';
require_once '../../includes/CSRF.php';
require_once '../../includes/session.php';
require_once '../../includes/config.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/authenticate.php';
if (isset($_POST['csrf_token'])) {
if (csrfValidateRequest() && !CSRFValidate()) {
handleInvalidCSRFToken();
}
// set installer path + options
$path = getenv("DOCUMENT_ROOT");
$opts = " --update --yes --path $path";
$installer = "sudo /etc/raspap/system/raspbian.sh";
$execUpdate = $installer.$opts;
// set installer path + options
$path = getenv("DOCUMENT_ROOT");
$opts = " --update --yes --check 0 --path $path";
$installer = "sudo /etc/raspap/system/raspbian.sh";
$execUpdate = $installer.$opts;
$response = shell_exec($execUpdate);
echo json_encode($response);
$response = shell_exec($execUpdate);
echo json_encode($response);
} else {
handleInvalidCSRFToken();
}

View File

@@ -1,8 +1,8 @@
<?php
require_once '../../includes/config.php';
require_once '../../includes/autoload.php';
require_once '../../includes/session.php';
require_once '../../src/RaspAP/Auth/HTTPAuth.php';
require_once '../../includes/config.php';
require_once '../../includes/authenticate.php';
$logFile = '/tmp/raspap_install.log';

View File

@@ -9,6 +9,7 @@ License: GNU General Public License v3.0
:root {
--raspap-content-main: #495057;
--raspap-text-muted: #858796;
--raspap-text-light: #999999;
--raspap-brand-color: #2b8080;
--raspap-offwhite: #faf9f6;
}
@@ -129,7 +130,7 @@ th {
.loading-spinner::before {
position: absolute;
top: 0;
top: 120px;
left: 0;
width: 100%;
height: calc(100vh / 4);
@@ -137,10 +138,10 @@ th {
justify-content: center;
align-items: center;
color: var(--raspap-text-muted);
content: "\f1ce"; /* Unicode for the circle-notch icon */
content: "\f1ce";
font-family: "Font Awesome 5 Free";
font-weight: 900; /* Adjust as needed */
font-size: 54px; /* Adjust icon size as needed */
font-weight: 900;
font-size: 54px;
animation: spin 1.2s linear infinite;
width: 100%;
}
@@ -168,12 +169,7 @@ th {
}
canvas#divDBChartBandwidthhourly {
height: 350px!important;
}
.chart-container {
height: 150px;
width: 200px;
height: 509px!important;
}
.dbChart {
@@ -190,7 +186,7 @@ canvas#divDBChartBandwidthhourly {
}
.check-progress {
color: #999;
color: var(--raspap-text-light);
}
.fa-check {
@@ -388,3 +384,290 @@ textarea.plugin-log {
font-family: monospace;
font-size: 0.9rem;
}
.card-wrapper {
margin: 1rem;
}
.dashboard-container {
display: flex;
position: relative;
width: 100%;
min-height: 400px;
padding: 2rem;
}
.connections-left,
.connections-right {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.connection-item {
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
z-index: 5;
color: var(--raspap-text-light);
}
.connection-right {
align-items: center;
margin-left: 10rem;
}
.connections-left i {
height: 40px;
display: flex;
align-items: center;
justify-content: left;
}
.connections-left i:first-child {
margin-top: 0;
}
.connections-left i:last-child {
margin-bottom: 0;
margin-left: 0.5rem;
}
.center-device {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
position: relative;
z-index: 1;
}
.center-device-top {
margin-bottom: 30px;
}
.client-group {
display: flex;
align-items: center;
flex-direction: row-reverse;
gap: 0.5rem;
}
.client-count {
text-align: right;
}
.clients-status {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
padding-right: 1rem;
}
.dashed-lines,
.solid-lines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: contain;
padding: 1rem;
left: 112px;
}
.dashed-lines-right,
.solid-lines-right {
left: -80px;
}
.solid-lines, .solid-lines-right {
z-index: 3;
}
.dashed-lines, .dashed-lines-right {
z-index 0;
}
.device-status {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
margin: 0.8rem 0;
}
.wifi-bands {
display: flex;
gap: 0.5rem;
}
.band {
padding: 0.25rem 1rem;
border: 2px solid var(--raspap-text-light);
border-radius: 4px;
background: transparent;
font-weight: 600;
color: var(--raspap-text-light);
}
.band.active {
border-color: var(--raspap-theme-color);
color: var(--raspap-theme-color);
}
.device-label {
font-size: 1.3rem;
text-align: center;
color: var(--raspap-theme-color);
margin-top: 1rem;
}
.status-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.3rem;
color: var(--raspap-text-light);
}
.bottom {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.3rem;
width: 100%;
}
.status-item .fa-stack {
width: 1.5em!important;
}
.connection-item>i {
color: var(--raspap-text-light);
}
.connection-item .fa-stack {
min-width: 2.5em;
}
.connections-left>.connection-item>span {
color: var(--raspap-text-light);
margin-right: 0.5rem;
}
.inactive {
color: var(--raspap-text-light)!important;
}
a.inactive:hover,
a.inactive:focus {
color: var(--raspap-text-light) !important;
}
@media (max-width: 1200px) {
.connection-item a > span:not(.fa-stack) {
display: none!important;
}
}
@media (max-width: 991px) {
.connections-right,
.connections-left {
display: none!important;
}
.dashboard-container {
width: auto;
padding: 0;
}
.device-status {
gap: 0.5rem;
}
.clients-mobile {
display: flex!important;
flex-direction: row!important;
}
}
.connection-item.active > span {
color: var(--raspap-theme-color)!important;
}
.connection-item.active > i {
color: var(--raspap-theme-color)!important;
}
.status-item.active > span {
color: var(--raspap-theme-color)!important;
}
.status-item.active > i {
color: var(--raspap-theme-color)!important;
}
.clients-mobile {
display: none;
flex-direction: column;
gap: 1rem;
margin-top: 2rem;
}
.client-type {
position: relative;
display: inline-flex;
align-items: center;
gap: 1rem;
}
.client-type i {
font-size: 1.5rem;
color: var(--raspap-theme-color);
width: 45px;
height: 45px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid var(--raspap-theme-color);
}
.client-type i.badge-icon {
font-size: 0.7rem;
background: var(--raspap-theme-color);
color: var(--raspap-offwhite);
width: 20px;
height: 20px;
border: none;
}
.client-count {
position: absolute;
top: -5px;
right: -5px;
background: var(--raspap-theme-color);
color: var(--raspap-offwhite);
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
}
.device-illustration {
min-width: 220px;
max-width: 250px;
}
.led-pulse {
opacity: 0.3 !important;
}
.hostapd-led {
color: #28a745;
opacity: 1;
transition: opacity 0.05s;
}

View File

@@ -14,9 +14,9 @@ License: GNU General Public License v3.0
@import url('all.css');
:root {
--raspap-theme-color: <?php echo $color; ?>;
--raspap-theme-lighter: <?php echo lightenColor($color, 20); ?>;
--raspap-theme-darker: <?php echo darkenColor($color, 20); ?>;
--raspap-theme-color: <?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>;
--raspap-theme-lighter: <?php echo htmlspecialchars(lightenColor($color, 20), ENT_QUOTES, 'UTF-8'); ?>;
--raspap-theme-darker: <?php echo htmlspecialchars(darkenColor($color, 20), ENT_QUOTES, 'UTF-8'); ?>;
}
body {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/dist/icons/mstile-150x150.png"/>
<TileColor>#b91d47</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

BIN
app/icons/favicon-96x96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
app/icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

3
app/icons/favicon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="240.000000pt" height="240.000000pt" viewBox="0 0 240.000000 240.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,240.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M867 2334 c-3 -3 -3 -20 -1 -37 2 -18 6 -54 8 -82 2 -27 5 -53 5 -56
1 -4 31 -6 69 -6 63 0 162 -9 207 -19 11 -2 31 -6 45 -8 14 -2 59 -14 100 -27
41 -13 80 -23 85 -24 6 0 12 -2 15 -5 3 -3 37 -20 75 -38 161 -78 335 -219
436 -357 55 -74 121 -175 115 -175 -3 0 10 -30 28 -67 18 -38 37 -81 41 -98 5
-16 9 -34 11 -40 27 -94 44 -173 48 -237 2 -15 19 -20 99 -23 95 -5 101 -1 86
60 -4 17 -10 48 -13 70 -27 197 -131 443 -258 613 -52 69 -252 272 -269 272
-5 0 -18 8 -28 18 -37 32 -50 42 -61 42 -5 0 -10 4 -10 8 0 4 -15 15 -32 23
-18 9 -53 27 -78 41 -95 52 -303 123 -390 133 -19 2 -46 7 -60 10 -34 8 -266
15 -273 9z"/>
<path d="M895 1867 c3 -50 6 -92 8 -93 1 -1 40 -4 87 -8 114 -8 139 -12 212
-37 272 -91 470 -300 547 -578 11 -39 17 -71 13 -71 -3 0 -2 -4 4 -10 5 -5 50
-12 100 -15 l91 -6 -3 26 c-15 141 -91 333 -182 460 -47 65 -152 170 -222 222
-164 122 -367 191 -582 198 l-79 2 6 -90z"/>
<path d="M747 1604 c-1 -2 -14 -5 -27 -7 -245 -47 -453 -199 -562 -412 -47
-91 -81 -232 -81 -340 -1 -88 19 -215 34 -215 5 0 6 -7 3 -15 -4 -8 -2 -21 3
-28 5 -6 20 -38 33 -70 12 -31 26 -57 31 -57 5 0 9 -6 9 -13 0 -7 9 -23 20
-35 11 -12 20 -25 20 -29 0 -12 80 -93 91 -93 6 0 8 -4 5 -8 -11 -18 215 -159
235 -147 5 4 9 2 9 -4 0 -20 189 -58 280 -56 87 2 213 25 245 44 6 3 12 6 15
6 21 3 50 18 50 26 0 6 5 7 10 4 13 -8 128 67 193 125 67 60 164 196 159 223
-1 4 1 7 6 7 8 0 31 47 36 76 3 11 9 32 15 47 24 62 33 251 17 344 -21 123
-93 271 -181 371 -49 54 -156 142 -174 142 -6 0 -11 4 -11 9 0 18 -180 85
-260 97 -43 7 -218 13 -223 8z m243 -199 c30 -9 62 -17 70 -18 8 -1 20 -5 25
-10 6 -4 37 -25 70 -46 71 -45 165 -135 174 -166 1 -5 15 -33 31 -61 78 -136
85 -368 15 -504 -8 -16 -15 -33 -14 -36 0 -4 -3 -10 -8 -13 -5 -3 -23 -26 -40
-51 -25 -37 -102 -113 -148 -148 -15 -12 -117 -59 -145 -68 -97 -31 -220 -35
-310 -11 -8 2 -28 7 -45 12 -16 4 -51 19 -77 32 -27 13 -51 24 -55 26 -4 1
-18 12 -31 24 -14 13 -30 20 -36 16 -6 -3 -8 -3 -4 1 9 10 -34 55 -46 48 -5
-3 -6 0 -2 6 3 6 -2 17 -12 24 -10 7 -27 28 -37 46 -11 17 -24 32 -29 32 -5 0
-6 3 -3 7 4 3 -2 20 -12 37 -27 43 -49 111 -56 176 -8 60 -7 158 0 170 3 4 7
25 10 46 5 45 56 152 97 205 15 20 28 42 28 48 0 6 4 10 8 8 4 -1 26 16 48 38
22 22 49 45 60 51 55 32 123 65 140 69 10 2 42 10 69 18 63 17 193 13 265 -8z"/>
<path d="M717 1231 c-299 -97 -383 -476 -154 -691 23 -22 48 -40 54 -40 7 0
13 -4 13 -9 0 -17 116 -53 185 -57 130 -8 228 29 320 121 49 49 66 75 89 135
33 89 38 197 11 270 -9 25 -17 50 -19 57 -6 30 -85 124 -133 157 -104 73 -248
95 -366 57z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,14 +1,21 @@
{
"name": "RaspAP",
"short_name": "RaspAP",
"icons": [
{
"src": "/app/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
"name": "RaspAP Admin Panel",
"short_name": "RaspAP",
"icons": [
{
"src": "/app/icons/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/app/icons/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

54
app/img/dashed.svg Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 227 596" style="enable-background:new 0 0 227 596;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#999999;stroke-width:3;}
.st1{fill:none;stroke:#999999;stroke-width:3;stroke-dasharray:6.0204,3.0102;}
.st2{fill:none;stroke:#999999;stroke-width:3;stroke-dasharray:5.7963,2.8981;}
</style>
<g id="dashed">
<g id="Line_1">
<g>
<line class="st0" x1="112.8" y1="0" x2="112.8" y2="3"/>
<line class="st1" x1="112.8" y1="6" x2="112.8" y2="591.5"/>
<line class="st0" x1="112.8" y1="593" x2="112.8" y2="596"/>
</g>
</g>
<g id="Line_2">
<g>
<line class="st0" x1="113.2" y1="0.8" x2="110.2" y2="0.8"/>
<line class="st2" x1="107.3" y1="0.8" x2="4.4" y2="0.8"/>
<line class="st0" x1="3" y1="0.8" x2="0" y2="0.8"/>
</g>
</g>
<g id="Line_3">
<g>
<line class="st0" x1="113.2" y1="198.9" x2="110.2" y2="198.9"/>
<line class="st2" x1="107.3" y1="198.9" x2="4.4" y2="198.9"/>
<line class="st0" x1="3" y1="198.9" x2="0" y2="198.9"/>
</g>
</g>
<g id="Line_4">
<g>
<line class="st0" x1="113.2" y1="397.1" x2="110.2" y2="397.1"/>
<line class="st2" x1="107.3" y1="397.1" x2="4.4" y2="397.1"/>
<line class="st0" x1="3" y1="397.1" x2="0" y2="397.1"/>
</g>
</g>
<g id="Line_5">
<g>
<line class="st0" x1="113.2" y1="595.2" x2="110.2" y2="595.2"/>
<line class="st2" x1="107.3" y1="595.2" x2="4.4" y2="595.2"/>
<line class="st0" x1="3" y1="595.2" x2="0" y2="595.2"/>
</g>
</g>
<g id="Line_6">
<g>
<line class="st0" x1="226.2" y1="297.8" x2="223.2" y2="297.8"/>
<line class="st2" x1="220.3" y1="297.8" x2="117.4" y2="297.8"/>
<line class="st0" x1="116" y1="297.8" x2="113" y2="297.8"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

1083
app/img/devices/compute.php Normal file

File diff suppressed because it is too large Load Diff

4450
app/img/devices/default.php Normal file

File diff suppressed because it is too large Load Diff

1561
app/img/devices/zero.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -32,20 +32,20 @@ $color = getColorOpt();
transform="translate(192.6768,123.4365)"
id="g20"><path
id="path22"
style="fill:<?php echo $color; ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
style="fill:<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 0,-37.169 -30.128,-67.3 -67.296,-67.3 -37.167,0 -67.294,30.131 -67.294,67.3 0,37.165 30.127,67.296 67.294,67.296 C -30.128,67.296 0,37.165 0,0" /></g><g
transform="translate(125.3823,219.0791)"
id="g24"><path
id="path26"
style="fill:<?php echo $color; ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
style="fill:<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -52.737,0 -95.641,-42.905 -95.641,-95.643 0,-52.74 42.904,-95.647 95.641,-95.647 52.737,0 95.642,42.907 95.642,95.647 C 95.642,-42.905 52.737,0 0,0 m 0,-217.29 c -67.073,0 -121.641,54.571 -121.641,121.647 C -121.641,-28.569 -67.073,26 0,26 67.074,26 121.642,-28.569 121.642,-95.643 121.642,-162.719 67.074,-217.29 0,-217.29" /></g><g
transform="translate(144.4277,271.9385)"
id="g28"><path
id="path30"
style="fill:<?php echo $color; ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
style="fill:<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 66.188,0 121.118,-49.055 130.392,-112.714 l 28.259,-1.874 C 150.044,-34.655 82.181,27.791 0,27.791 c -3.892,0 -7.75,-0.147 -11.571,-0.423 L -9.73,-0.397 C -6.513,-0.161 -3.275,0 0,0" /></g><g
transform="translate(144.4883,334.7588)"
id="g32"><path
id="path34"
style="fill:<?php echo $color; ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
style="fill:<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 101.94,0 185.667,-79.438 192.56,-179.664 l 27.962,-1.857 C 214.513,-65.087 117.899,27.791 0,27.791 c -5.31,0 -10.576,-0.2 -15.792,-0.571 l 1.84,-27.728 C -9.343,-0.177 -4.691,0 0,0" /></g></g></g></g></svg>

40
app/img/right-dashed.svg Normal file
View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 314 594" style="enable-background:new 0 0 314 594;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#999999;stroke-width:3;}
.st1{fill:none;stroke:#999999;stroke-width:3;stroke-dasharray:6.04,3.02;}
.st2{fill:none;stroke:#999999;stroke-width:3;stroke-dasharray:5.7963,2.8981;}
</style>
<g id="dashed">
<g id="horizontal">
<g>
<line class="st0" x1="113.2" y1="144" x2="113.2" y2="147"/>
<line class="st1" x1="113.2" y1="150" x2="113.3" y2="447.5"/>
<line class="st0" x1="113.3" y1="449" x2="113.3" y2="452"/>
</g>
</g>
<g id="top">
<g>
<line class="st0" x1="114" y1="144.8" x2="117" y2="144.8"/>
<line class="st2" x1="119.9" y1="144.8" x2="222.8" y2="144.8"/>
<line class="st0" x1="224.2" y1="144.8" x2="227.2" y2="144.8"/>
</g>
</g>
<g id="out">
<g>
<line class="st0" x1="0" y1="297.8" x2="3" y2="297.8"/>
<line class="st2" x1="5.9" y1="297.8" x2="108.8" y2="297.8"/>
<line class="st0" x1="110.2" y1="297.8" x2="113.2" y2="297.8"/>
</g>
</g>
<g id="bottom">
<g>
<line class="st0" x1="113" y1="450.8" x2="116" y2="450.8"/>
<line class="st2" x1="118.9" y1="450.8" x2="221.8" y2="450.8"/>
<line class="st0" x1="223.2" y1="450.8" x2="226.2" y2="450.8"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

46
app/img/right-solid.php Normal file
View File

@@ -0,0 +1,46 @@
<?php
header("Content-Type: image/svg+xml");
$showDevice1 = isset($_GET['device-1']);
$showOut = isset($_GET['out']);
$showDevice2 = isset($_GET['device-2']);
?>
<svg width="313" height="594" viewBox="0 0 313 594" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame 1">
<g id="right connection frame">
<g id="solid">
<?php if ($showDevice2): ?>
<line id="joint-device-2" y1="-0.75" x2="154" y2="-0.75"
transform="matrix(4.37114e-08 1 1 -4.37114e-08 114 297)"
stroke="#008281" stroke-width="4"/>
<?php endif; ?>
<?php if ($showDevice1): ?>
<line id="joint-device-1" style="display: inline;"
y1="-0.75" x2="154" y2="-0.75"
transform="matrix(4.37114e-08 1 1 -4.37114e-08 114 144)"
stroke="#008281" stroke-width="4"/>
<line id="device-1" style="display: inline;"
y1="-0.75" x2="113.231" y2="-0.75"
transform="matrix(1 8.74228e-08 8.74228e-08 -1 114 144)"
stroke="#008281" stroke-width="4"/>
<?php endif; ?>
<?php if ($showOut): ?>
<line id="out" style="display: inline;"
y1="-0.75" x2="113.231" y2="-0.75"
transform="matrix(1 8.74228e-08 8.74228e-08 -1 -0.000305176 297)"
stroke="#008281" stroke-width="4"/>
<?php endif; ?>
<?php if ($showDevice2): ?>
<line id="device-2" style="display: inline;"
y1="-0.75" x2="113.231" y2="-0.75"
transform="matrix(1 8.74228e-08 8.74228e-08 -1 113 450)"
stroke="#008281" stroke-width="4"/>
<?php endif; ?>
</g>
</g>
</g>
</svg>

65
app/img/solid.php Normal file
View File

@@ -0,0 +1,65 @@
<?php
header("Content-Type: image/svg+xml");
require_once '../../includes/functions.php';
$color = getColorOpt();
$showJoint = isset($_GET['joint']);
$showDevice1 = isset($_GET['device-1']);
$showOut = isset($_GET['out']);
$showDevice2 = isset($_GET['device-2']);
$showDevice3 = isset($_GET['device-3']);
$showDevice4 = isset($_GET['device-4']);
?>
<svg xmlns="http://www.w3.org/2000/svg" width="227" height="596" viewBox="0 0 227 596" fill="none">
<?php
// Device positions array (y-coordinates)
$devicePositions = [
'device-1' => 0.75,
'out' => 297.75,
'device-2' => 198.75,
'device-3' => 397.058,
'device-4' => 595.211
];
// Calculate joint line segments
if ($showJoint) {
$activeDevices = array_filter([$showDevice1, $showDevice2, $showDevice3, $showDevice4]);
$activeYs = [];
foreach ($devicePositions as $device => $y) {
if (isset($_GET[$device])) {
$activeYs[] = $y;
}
}
// Add top/bottom if first/last device is connected
if ($showDevice1) array_unshift($activeYs, 0);
if ($showDevice4) $activeYs[] = 596;
// Draw segments between consecutive points
for ($i = 1; $i < count($activeYs); $i++) {
$y1 = $activeYs[$i-1];
$y2 = $activeYs[$i];
echo "<line x1='112.75' y1='$y1' x2='112.75' y2='$y2' stroke='" . htmlspecialchars($color, ENT_QUOTES, 'UTF-8') . "' stroke-width='4'/>";
}
}
?>
<?php if ($showDevice1): ?>
<line x1="113.231" y1="0.75" x2="7.69496e-06" y2="0.75001" stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="6" id="device-1"/>
<?php endif; ?>
<?php if ($showOut): ?>
<line x1="226.231" y1="297.75" x2="113" y2="297.75" stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4" id="out"/>
<?php endif; ?>
<?php if ($showDevice2): ?>
<line x1="113.231" y1="198.75" x2="7.69496e-06" y2="198.75" stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4" id="device-2"/>
<?php endif; ?>
<?php if ($showDevice3): ?>
<line x1="113.231" y1="397.058" x2="7.69496e-06" y2="397.058" stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4" id="device-3"/>
<?php endif; ?>
<?php if ($showDevice4): ?>
<line x1="113.231" y1="595.211" x2="7.69496e-06" y2="595.211" stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4" id="device-4"/>
<?php endif; ?>
</svg>

29
app/img/uri-qr-code.php Executable file
View File

@@ -0,0 +1,29 @@
<?php
if (!isset($_GET['uri']) || !filter_var($_GET['uri'], FILTER_VALIDATE_URL)) {
header("HTTP/1.1 400 Bad Request");
exit("Invalid or missing URI parameter");
}
$uri = $_GET['uri'];
$command = "qrencode -t svg -m 0 -o - " . escapeshellarg($uri);
$svg = shell_exec($command);
if ($svg === null) {
error_log("QR generation failed for URI: $uri");
header("HTTP/1.1 500 Internal Server Error");
exit("Failed to generate QR code");
}
$etag = hash('sha256', $uri);
$content_length = strlen($svg);
$last_modified = gmdate("D, d M Y H:i:s") . " GMT";
header("Content-Type: image/svg+xml");
header("Content-Length: $content_length");
header("Last-Modified: $last_modified");
header("ETag: \"$etag\"");
header("X-QR-Code-Content: " . htmlspecialchars($uri, ENT_QUOTES, 'UTF-8'));
echo $svg;

View File

@@ -13,6 +13,7 @@ if (!isset($_SERVER['HTTP_REFERER'])) {
exec("sudo cat " .RASPI_WIREGUARD_PATH.'client.conf', $return);
$peer_conf = implode(PHP_EOL,$return);
$peer_conf.= PHP_EOL;
$peer_conf_sanitized = str_replace(["\r", "\n"], '', $peer_conf);
$command = "qrencode -t svg -m 0 -o - " . mb_escapeshellarg($peer_conf);
$svg = shell_exec($command);
$etag = hash('sha256', $peer_conf);
@@ -23,6 +24,6 @@ header("Content-Type: image/svg+xml");
header("Content-Length: $content_length");
header("Last-Modified: $last_modified");
header("ETag: \"$etag\"");
header("X-QR-Code-Content: $peer_conf");
header("X-QR-Code-Content: $peer_conf_sanitized");
echo shell_exec($command);

View File

@@ -12,12 +12,12 @@ if (!isset($_SERVER['HTTP_REFERER'])) {
$hostapd = parse_ini_file(RASPI_HOSTAPD_CONFIG, false, INI_SCANNER_RAW);
// assume wpa encryption and get the passphrase
// assume WPA encryption and get the passphrase
$type = "WPA";
$password = isset($hostapd['wpa_psk']) ? $hostapd['wpa_psk'] : $hostapd['wpa_passphrase'];
// use wep if configured
$wep_default_key = intval($hostapd['wep_default_key']);
// use WEP if configured
$wep_default_key = intval($hostapd['wep_default_key'] ?? 0);
$wep_key = 'wep_key' . $wep_default_key;
if (array_key_exists($wep_key, $hostapd)) {
$type = "WEP";
@@ -30,7 +30,7 @@ if (empty($password)) {
}
$ssid = $hostapd['ssid'];
$hidden = intval($hostapd['ignore_broadcast_ssid']) != 0 ? "H:true" : "";
$hidden = intval($hostapd['ignore_broadcast_ssid'] ?? 0) !== 0 ? "H:true" : "";
$ssid = qr_encode($ssid);
$password = qr_encode($password);

555
app/js/ajax/main.js Normal file
View File

@@ -0,0 +1,555 @@
function loadSummary(strInterface) {
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/networking/get_ip_summary.php',{'interface': strInterface, 'csrf_token': csrfToken},function(data){
jsonData = JSON.parse(data);
if(jsonData['return'] == 0) {
$('#'+strInterface+'-summary').html(jsonData['output'].join('<br />'));
} else if(jsonData['return'] == 2) {
$('#'+strInterface+'-summary').append('<div class="alert alert-danger alert-dismissible" role="alert"><button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>'+jsonData['output'].join('<br />')+'</div>');
}
});
}
function getAllInterfaces() {
$.get('ajax/networking/get_all_interfaces.php',function(data){
jsonData = JSON.parse(data);
$.each(jsonData,function(ind,value){
loadSummary(value)
});
});
}
$(document).on("click", "#js-clearhostapd-log", function(e) {
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/logging/clearlog.php?',{'logfile':'/tmp/hostapd.log', 'csrf_token': csrfToken},function(data){
jsonData = JSON.parse(data);
$("#hostapd-log").val("");
});
});
$(document).on("click", "#js-cleardnsmasq-log", function(e) {
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/logging/clearlog.php?',{'logfile':'/var/log/dnsmasq.log', 'csrf_token': csrfToken},function(data){
jsonData = JSON.parse(data);
$("#dnsmasq-log").val("");
});
});
$(document).on("click", "#js-clearopenvpn-log", function(e) {
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/logging/clearlog.php?',{'logfile':'/tmp/openvpn.log', 'csrf_token': csrfToken},function(data){
jsonData = JSON.parse(data);
$("#openvpn-log").val("");
});
});
function loadWifiStations(refresh) {
return function() {
var complete = function() { $(this).removeClass('loading-spinner'); }
var qs = refresh === true ? '?refresh' : '';
$('.js-wifi-stations')
.addClass('loading-spinner')
.empty()
.load('ajax/networking/wifi_stations.php'+qs, complete);
};
}
$(".js-reload-wifi-stations").on("click", loadWifiStations(true));
/*
Populates the DHCP server form fields
Option toggles are set dynamically depending on the loaded configuration
*/
function loadInterfaceDHCPSelect() {
var strInterface = $('#cbxdhcpiface').val();
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/networking/get_netcfg.php', {'iface' : strInterface, 'csrf_token': csrfToken}, function(data){
jsonData = JSON.parse(data);
$('#dhcp-iface')[0].checked = jsonData.DHCPEnabled;
$('#txtipaddress').val(jsonData.StaticIP);
$('#txtsubnetmask').val(jsonData.SubnetMask);
$('#txtgateway').val(jsonData.StaticRouters);
$('#chkfallback')[0].checked = jsonData.FallbackEnabled;
$('#default-route').prop('checked', jsonData.DefaultRoute);
if (strInterface.startsWith("wl")) {
$('#nohook-wpa-supplicant').parent().parent().parent().show()
$('#nohook-wpa-supplicant').prop('checked', jsonData.NoHookWPASupplicant);
} else {
$('#nohook-wpa-supplicant').parent().parent().parent().hide()
}
$('#txtrangestart').val(jsonData.RangeStart);
$('#txtrangeend').val(jsonData.RangeEnd);
$('#txtrangeleasetime').val(jsonData.leaseTime);
$('#txtdns1').val(jsonData.DNS1);
$('#txtdns2').val(jsonData.DNS2);
$('#cbxrangeleasetimeunits').val(jsonData.leaseTimeInterval);
$('#no-resolv')[0].checked = jsonData.upstreamServersEnabled;
$('#cbxdhcpupstreamserver').val(jsonData.upstreamServers[0]);
$('#txtmetric').val(jsonData.Metric);
if (jsonData.StaticIP !== null && jsonData.StaticIP !== '' && !jsonData.FallbackEnabled) {
$('#chkstatic').prop('checked', true).closest('.btn').addClass('active');
$('#chkdhcp').prop('checked', false).closest('.btn').removeClass('active');
$('#chkfallback').prop('disabled', true);
$('#dhcp-iface').removeAttr('disabled');
} else {
$('#chkdhcp').closest('.btn').addClass('active');
$('#chkdhcp').closest('.btn').blur();
}
if (jsonData.FallbackEnabled || $('#chkdhcp').is(':checked')) {
$('#dhcp-iface').prop('disabled', true);
setDhcpFieldsDisabled();
}
const leaseContainer = $('.js-dhcp-static-lease-container');
leaseContainer.empty();
if (jsonData.dhcpHost && jsonData.dhcpHost.length > 0) {
const leases = jsonData.dhcpHost || [];
leases.forEach((entry, index) => {
const [mainPart, commentPart] = entry.split('#');
const comment = commentPart ? commentPart.trim() : '';
const [mac, ip] = mainPart.split(',').map(part => part.trim());
const row = `
<div class="row dhcp-static-lease-row js-dhcp-static-lease-row">
<div class="col-md-4 col-xs-3">
<input type="text" name="static_leases[mac][]" value="${mac}" placeholder="MAC address" class="form-control">
</div>
<div class="col-md-3 col-xs-3">
<input type="text" name="static_leases[ip][]" value="${ip}" placeholder="IP address" class="form-control">
</div>
<div class="col-md-3 col-xs-3">
<input type="text" name="static_leases[comment][]" value="${comment || ''}" placeholder="Optional comment" class="form-control">
</div>
<div class="col-md-2 col-xs-3">
<button type="button" class="btn btn-outline-danger js-remove-dhcp-static-lease"><i class="far fa-trash-alt"></i></button>
</div>
</div>`;
leaseContainer.append(row);
});
}
});
}
$('#debugModal').on('shown.bs.modal', function (e) {
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/system/sys_debug.php',{'csrf_token': csrfToken},function(data){
window.location.replace('/ajax/system/sys_get_logfile.php');
$('#debugModal').modal('hide');
});
});
$('#chkupdateModal').on('shown.bs.modal', function (e) {
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/system/sys_chk_update.php',{'csrf_token': csrfToken},function(data){
var response = JSON.parse(data);
var tag = response.tag;
var update = response.update;
var msg;
var msgUpdate = $('#msgUpdate').data('message');
var msgLatest = $('#msgLatest').data('message');
var msgInstall = $('#msgInstall').data('message');
var msgDismiss = $('#js-check-dismiss').data('message');
var faCheck = '<i class="fas fa-check ms-2"></i><br />';
$("#updateSync").removeClass("fa-spin");
if (update === true) {
msg = msgUpdate +' '+tag;
$("#msg-check-update").html(msg);
$("#msg-check-update").append(faCheck);
$("#msg-check-update").append("<p>"+msgInstall+"</p>");
$("#js-sys-check-update").removeClass("collapse");
} else {
msg = msgLatest;
dismiss = $("#js-check-dismiss");
$("#msg-check-update").html(msg);
$("#msg-check-update").append(faCheck);
$("#js-sys-check-update").remove();
dismiss.text(msgDismiss);
dismiss.removeClass("btn-outline-secondary");
dismiss.addClass("btn-primary");
}
});
});
$('#performUpdate').on('submit', function(event) {
event.preventDefault();
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/system/sys_perform_update.php',{
'csrf_token': csrfToken
})
$('#chkupdateModal').modal('hide');
$('#performupdateModal').modal('show');
});
function fetchUpdateResponse() {
const complete = 6;
const error = 7;
let phpFile = 'ajax/system/sys_read_logfile.php';
$.ajax({
url: phpFile,
type: 'GET',
success: function(response) {
for (let i = 1; i <= 6; i++) {
let divId = '#updateStep' + i;
if (response.includes(i.toString())) {
$(divId).removeClass('invisible');
}
}
// check if the update is complete or if there's an error
if (response.includes(complete)) {
var successMsg = $('#successMsg').data('message');
$('#updateMsg').after('<span class="small">' + successMsg + '</span>');
$('#updateMsg').addClass('fa-check');
$('#updateMsg').removeClass('invisible');
$('#updateStep6').removeClass('invisible');
$('#updateSync2').removeClass("fa-spin");
$('#updateOk').removeAttr('disabled');
} else if (response.includes(error)) {
var errorMsg = $('#errorMsg').data('message');
$('#updateMsg').after('<span class="small">' + errorMsg + '</span>');
$('#updateMsg').addClass('fa-times');
$('#updateMsg').removeClass('invisible');
$('#updateSync2').removeClass("fa-spin");
$('#updateOk').removeAttr('disabled');
} else {
setTimeout(fetchUpdateResponse, 500);
}
},
error: function(xhr, status, error) {
console.error("AJAX Error:", error);
}
});
}
$('#ovpn-confirm-delete').on('click', '.btn-delete', function (e) {
var cfg_id = $(this).data('recordId');
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/openvpn/del_ovpncfg.php',{'cfg_id':cfg_id, 'csrf_token': csrfToken},function(data){
jsonData = JSON.parse(data);
$("#ovpn-confirm-delete").modal('hide');
var row = $(document.getElementById("openvpn-client-row-" + cfg_id));
row.fadeOut( "slow", function() {
row.remove();
});
});
});
$('#ovpn-confirm-delete').on('show.bs.modal', function (e) {
var data = $(e.relatedTarget).data();
$('.btn-delete', this).data('recordId', data.recordId);
});
$('#ovpn-confirm-activate').on('click', '.btn-activate', function (e) {
var cfg_id = $(this).data('record-id');
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/openvpn/activate_ovpncfg.php',{'cfg_id':cfg_id, 'csrf_token': csrfToken},function(data){
jsonData = JSON.parse(data);
$("#ovpn-confirm-activate").modal('hide');
setTimeout(function(){
window.location.reload();
},300);
});
});
$('#js-system-reset-confirm').on('click', function (e) {
var progressText = $('#js-system-reset-confirm').attr('data-message');
var successHtml = $('#system-reset-message').attr('data-message');
var closeHtml = $('#js-system-reset-cancel').attr('data-message');
var csrfToken = $('meta[name=csrf_token]').attr('content');
var progressHtml = $('<div>').text(progressText).html() + '<i class="fas fa-cog fa-spin ms-2"></i>';
$('#system-reset-message').html(progressHtml);
$.post('ajax/networking/do_sys_reset.php?',{'csrf_token':csrfToken},function(data){
setTimeout(function(){
jsonData = JSON.parse(data);
if(jsonData['return'] == 0) {
$('#system-reset-message').text(successHtml);
} else {
$('#system-reset-message').text('Error occured: '+ jsonData['return']);
}
$("#js-system-reset-confirm").hide();
$("#js-system-reset-cancel").text(closeHtml);
},750);
});
});
$('#js-sys-reboot, #js-sys-shutdown').on('click', function (e) {
e.preventDefault();
var csrfToken = $('meta[name=csrf_token]').attr('content');
var action = $(this).data('action');
$.post('ajax/system/sys_actions.php?',{'a': action, 'csrf_token': csrfToken},function(data){
var response = JSON.parse(data);
});
});
$('#js-install-plugin-confirm').on('click', function (e) {
var button = $('#install-user-plugin').data('button');
var manifestData = button.data('plugin-manifest');
var installPath = manifestData.install_path;
var pluginUri = manifestData.plugin_uri;
var pluginVersion = manifestData.version;
var pluginConfirm = $('#js-install-plugin-confirm').text();
var progressText = $('#js-install-plugin-confirm').attr('data-message');
var successHtml = $('#plugin-install-message').attr('data-message');
var successText = $('<div>').text(successHtml).text();
var csrfToken = $('meta[name=csrf_token]').attr('content');
if (pluginConfirm === 'Install now') {
$("#install-user-plugin").modal('hide');
$("#install-plugin-progress").modal('show');
$.post(
'ajax/plugins/do_plugin_install.php',
{
'plugin_uri': pluginUri,
'plugin_version': pluginVersion,
'install_path': installPath,
'csrf_token': csrfToken
},
function (data) {
setTimeout(function () {
response = JSON.parse(data);
if (response === true) {
$('#plugin-install-message').contents().first().text(successText);
$('#plugin-install-message')
.find('i')
.removeClass('fas fa-cog fa-spin link-secondary')
.addClass('fas fa-check');
$('#js-install-plugin-ok').removeAttr("disabled");
} else {
const errorMessage = jsonData.error || 'An unknown error occurred.';
var errorLog = '<textarea class="plugin-log text-secondary" readonly>' + errorMessage + '</textarea>';
$('#plugin-install-message')
.contents()
.first()
.replaceWith('An error occurred installing the plugin:');
$('#plugin-install-message').append(errorLog);
$('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary');
$('#js-install-plugin-ok').removeAttr("disabled");
}
}, 200);
}
).fail(function (xhr) {
const jsonData = JSON.parse(xhr.responseText);
const errorMessage = jsonData.error || 'An unknown error occurred.';
$('#plugin-install-message')
.contents()
.first()
.replaceWith('An error occurred installing the plugin:');
var errorLog = '<textarea class="plugin-log text-secondary" readonly>' + errorMessage + '</textarea>';
$('#plugin-install-message').append(errorLog);
$('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary');
$('#js-install-plugin-ok').removeAttr("disabled");
});
} else if (pluginConfirm === 'Get Insiders') {
window.open('https://docs.raspap.com/insiders/', '_blank');
return;
} else if (pluginConfirm === 'OK') {
$("#install-user-plugin").modal('hide');
}
});
// Retrieves the 'channel' value specified in hostapd.conf
function getChannel() {
$.get('ajax/networking/get_channel.php',function(data){
jsonData = JSON.parse(data);
loadChannelSelect(jsonData);
});
}
/*
Sets the wirelss channel select options based on frequencies reported by iw.
See: https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git
Also: https://en.wikipedia.org/wiki/List_of_WLAN_channels
*/
function loadChannelSelect(selected) {
var iface = $('#cbxinterface').val();
var hwmodeText = '';
var csrfToken = $('meta[name=csrf_token]').attr('content');
// update hardware mode tooltip
setHardwareModeTooltip();
$.post('ajax/networking/get_frequencies.php',{'interface': iface, 'csrf_token': csrfToken, 'selected': selected},function(response){
var hw_mode = $('#cbxhwmode').val();
var country_code = $('#cbxcountries').val();
var channel_select = $('#cbxchannel');
var btn_save = $('#btnSaveHostapd');
var data = JSON.parse(response);
var selectableChannels = [];
// Map selected hw_mode to available channels
if (hw_mode === 'a') {
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
} else if (hw_mode !== 'ac') {
selectableChannels = data.filter(item => item.MHz.toString().startsWith('24'));
} else if (hw_mode === 'b') {
selectableChannels = data.filter(item => item.MHz.toString().startsWith('24'));
} else if (hw_mode === 'ac') {
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
}
// If selected channel doeesn't exist in allowed channels, set default or null (unsupported)
if (!selectableChannels.find(item => item.Channel === selected)) {
if (selectableChannels.length === 0) {
selectableChannels[0] = { Channel: null };
} else {
defaultChannel = selectableChannels[0].Channel;
selected = defaultChannel
}
}
// Set channel select with available values
channel_select.empty();
if (selectableChannels[0].Channel === null) {
channel_select.append($("<option></option>").attr("value", "").text("---"));
channel_select.prop("disabled", true);
btn_save.prop("disabled", true);
} else {
channel_select.prop("disabled", false);
btn_save.prop("disabled", false);
$.each(selectableChannels, function(key,value) {
channel_select.append($("<option></option>").attr("value", value.Channel).text(value.Channel));
});
channel_select.val(selected);
}
});
}
/* Sets hardware mode tooltip text for selected interface
* and calls loadChannelSelect()
*/
function setHardwareModeTooltip() {
var iface = $('#cbxinterface').val();
var hwmodeText = '';
var csrfToken = $('meta[name=csrf_token]').attr('content');
// Explanatory text if 802.11ac is disabled
if ($('#cbxhwmode').find('option[value="ac"]').prop('disabled') == true ) {
var hwmodeText = $('#hwmode').attr('data-tooltip');
}
$.post('ajax/networking/get_nl80211_band.php?',{'interface': iface, 'csrf_token': csrfToken},function(data){
var responseText = JSON.parse(data);
$('#tiphwmode').attr('data-original-title', responseText + '\n' + hwmodeText );
});
}
/* Updates the selected blocklist
* Request is passed to an ajax handler to download the associated list.
* Interface elements are updated to indicate current progress, status.
*/
function updateBlocklist() {
const opt = $('#cbxblocklist option:selected');
const blocklist_id = opt.val();
const csrfToken = $('meta[name=csrf_token]').attr('content');
if (blocklist_id === '') return;
const statusIcon = $('#cbxblocklist-status').find('i');
const statusWrapper = $('#cbxblocklist-status');
statusIcon.removeClass('fa-check fa-exclamation-triangle').addClass('fa-cog fa-spin');
statusWrapper.removeClass('check-hidden check-error check-updated').addClass('check-progress');
$.post('ajax/adblock/update_blocklist.php', {
'blocklist_id': blocklist_id,
'csrf_token': csrfToken
}, function (data) {
let jsonData;
try {
jsonData = JSON.parse(data);
} catch (e) {
showError("Unexpected server response.");
return;
}
const resultCode = jsonData['return'];
const output = jsonData['output']?.join('\n') || '';
switch (resultCode) {
case 0:
statusIcon.removeClass('fa-cog fa-spin').addClass('fa-check');
statusWrapper.removeClass('check-progress').addClass('check-updated').delay(500).animate({ opacity: 1 }, 700);
$('#blocklist-' + jsonData['list']).text("Just now");
break;
case 1:
showError("Invalid blocklist.");
break;
case 2:
showError("No blocklist provided.");
break;
case 3:
showError("Could not parse blocklists.json.");
break;
case 4:
showError("blocklists.json file not found.");
break;
case 5:
showError("Update script not found.");
break;
default:
showError("Unknown error occurred.");
}
}).fail(function (jqXHR, textStatus, errorThrown) {
showError(`AJAX request failed: ${textStatus}`);
});
function showError(message) {
statusIcon.removeClass('fa-cog fa-spin').addClass('fa-exclamation-triangle');
statusWrapper.removeClass('check-progress').addClass('check-error');
alert("Blocklist update failed:\n\n" + message);
}
}
function clearBlocklistStatus() {
$('#cbxblocklist-status').removeClass('check-updated').addClass('check-hidden');
}
// Handler for the WireGuard generate key button
$('.wg-keygen').click(function(){
var parentGroup = $(this).closest('.input-group');
var entity_pub = parentGroup.find('input[type="text"]');
var updated = entity_pub.attr('name')+"-pubkey-status";
var csrfToken = $('meta[name="csrf_token"]').attr('content');
$.post('ajax/networking/get_wgkey.php',{'entity':entity_pub.attr('name'), 'csrf_token': csrfToken},function(data){
var jsonData = JSON.parse(data);
entity_pub.val(jsonData.pubkey);
$('#' + updated).removeClass('check-hidden').addClass('check-updated').delay(500).animate({ opacity: 1 }, 700);
});
});
// Handler for wireguard client.conf download
$('.wg-client-dl').click(function(){
var req = new XMLHttpRequest();
var url = 'ajax/networking/get_wgcfg.php';
req.open('get', url, true);
req.responseType = 'blob';
req.setRequestHeader('Content-type', 'text/plain; charset=UTF-8');
req.onreadystatechange = function (event) {
if(req.readyState == 4 && req.status == 200) {
var blob = req.response;
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download = 'client.conf';
link.click();
}
}
req.send();
})
let sessionCheckInterval = setInterval(checkSession, 5000);
function checkSession() {
// skip session check if on login page
if (window.location.pathname === '/login') {
return;
}
var csrfToken = $('meta[name=csrf_token]').attr('content');
$.post('ajax/session/do_check_session.php',{'csrf_token': csrfToken},function (data) {
if (data.status === 'session_expired') {
clearInterval(sessionCheckInterval);
showSessionExpiredModal();
}
}).fail(function (jqXHR, status, err) {
console.error("Error checking session status:", status, err);
});
}

View File

@@ -1,7 +0,0 @@
/*!
* RaspAP - RaspAP WiFi Configuration Portal v1.6.1 (https://github.com/billz/raspap-webgui)
* Copyright 2013-2019 RaspAP Developers
* Licensed under MIT (https://github.com/raspap-webgui/raspap-webgui/blob/master/LICENSE)
*/
!function(r,i){"use strict";function t(t){r("#divChartBandwidthhourly").empty(),r("#divChartBandwidthdaily").empty(),r("#divChartBandwidthmonthly").empty(),r("#divTableBandwidthhourly").empty(),r("#divTableBandwidthdaily").empty(),r("#divTableBandwidthmonthly").empty();var e=r("ul#tabbarBandwidth li.active a").attr("href").substr(1),a="ajax/bandwidth/get_bandwidth.php?";a+="inet=",a+=encodeURIComponent(r("#cbxInterface"+e+" option:selected").text()),a+="&tu=",a+=encodeURIComponent(e.substr(0,1));var d="mb";a+="&dsu="+encodeURIComponent(d);var n=function(t,e){return new Morris.Bar({element:t,xkey:"date",ykeys:["rx","tx"],labels:[i.receive+" "+e.toUpperCase(),i.send+" "+e.toUpperCase()]})}("divChartBandwidth"+e,d);!function(t,e){r("#"+t).append('<table id="tableBandwidth'+e+'" class="table table-responsive table-striped container-fluid"><thead><tr><th>date</th><th>rx</th><th>tx</th></tr></thead><tbody></tbody></table>')}("divTableBandwidth"+e,e);r.ajax({url:a,dataType:"json",beforeSend:function(){r("#divLoaderBandwidth"+e).removeClass("hidden")}}).done(function(t){r("#divLoaderBandwidth"+e).addClass("hidden"),n.setData(t),r("#tableBandwidth"+e).DataTable({searching:!1,paging:!1,data:t,order:[[0,"ASC"]],columns:[{data:"date"},{data:"rx",title:i.receive+" "+d.toUpperCase()},{data:"tx",title:i.send+" "+d.toUpperCase()}]})}).fail(function(t,e){window.console?console.error("server error"):alert("server error")})}r(document).ready(function(){r('#tabbarBandwidth a[data-toggle="tab"]').on("shown.bs.tab",t),r("#cbxInterfacehourly").on("change",t),r("#cbxInterfacedaily").on("change",t),r("#cbxInterfacemonthly").on("change",t),t()})}(jQuery,t);

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
/*!
* RaspAP - RaspAP WiFi Configuration Portal v1.6.1 (https://github.com/billz/raspap-webgui)
* Copyright 2013-2019 RaspAP Developers
* Licensed under MIT (https://github.com/raspap-webgui/raspap-webgui/blob/master/LICENSE)
*/
function msgShow(t,a){if(0==t)var e="success";else if(2==t||1==t)e="danger";return'<div class="alert alert-'+e+' alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>'+a+"</div>"}function createNetmaskAddr(t){var a=[];for(i=0;i<4;i++){var e=Math.min(t,8);a.push(256-Math.pow(2,8-e)),t-=e}return a.join(".")}function loadSummary(a){$.post("/ajax/networking/get_ip_summary.php",{interface:a},function(t){jsonData=JSON.parse(t),console.log(jsonData),0==jsonData.return?$("#"+a+"-summary").html(jsonData.output.join("<br />")):2==jsonData.return&&$("#"+a+"-summary").append('<div class="alert alert-danger alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>'+jsonData.output.join("<br />")+"</div>")})}function getAllInterfaces(){$.get("/ajax/networking/get_all_interfaces.php",function(t){jsonData=JSON.parse(t),$.each(jsonData,function(t,a){loadSummary(a)})})}function setupTabs(){$('a[data-toggle="tab"]').on("shown.bs.tab",function(t){var a=$(t.target).attr("href");a.match("summary")||loadCurrentSettings(a.replace("#",""))})}function loadCurrentSettings(t){$.post("/ajax/networking/get_int_config.php",{interface:t},function(t){jsonData=JSON.parse(t),$.each(jsonData.output,function(t,a){var n=a.interface;$.each(a,function(t,a){switch(t){case"static":"true"==a?($("#"+n+"-static").click(),$("#"+n+"-nofailover").click()):$("#"+n+"-dhcp").click();break;case"failover":"true"===a?$("#"+n+"-failover").click():$("#"+n+"-nofailover").click();break;case"ip_address":var e=a.split("/");$("#"+n+"-ipaddress").val(e[0]),$("#"+n+"-netmask").val(createNetmaskAddr(e[1]));break;case"routers":$("#"+n+"-gateway").val(a);break;case"domain_name_server":svrsDNS=a.split(" "),$("#"+n+"-dnssvr").val(svrsDNS[0]),$("#"+n+"-dnssvralt").val(svrsDNS[1])}})})})}function saveNetworkSettings(t){var a=$("#frm-"+t).find(":input"),e={};$.each(a,function(t,a){"radio"==$(a).attr("type")?e[$(a).attr("id")]=$(a).prop("checked"):e[$(a).attr("id")]=$(a).val()}),e.interface=t,$.post("/ajax/networking/save_int_config.php",e,function(t){var a=JSON.parse(t);$("#msgNetworking").html(msgShow(a.return,a.output))})}function applyNetworkSettings(){$(this).data("int");arrFormData={generate:""},$.post("/ajax/networking/gen_int_config.php",arrFormData,function(t){console.log(t);var a=JSON.parse(t);$("#msgNetworking").html(msgShow(a.return,a.output))})}function setupBtns(){$("#btnSummaryRefresh").click(function(){getAllInterfaces()}),$(".intsave").click(function(){saveNetworkSettings($(this).data("int"))}),$(".intapply").click(function(){applyNetworkSettings()})}function setCSRFTokenHeader(t,a,e){var n=$("meta[name=csrf_token]").attr("content");/^(POST|PATCH|PUT|DELETE)$/i.test(e.type)&&a.setRequestHeader("X-CSRF-Token",n)}function contentLoaded(){switch(pageCurrent=window.location.href.split("?")[1].split("=")[1],pageCurrent=pageCurrent.replace("#",""),$("#side-menu").metisMenu(),pageCurrent){case"network_conf":getAllInterfaces(),setupTabs(),setupBtns()}}function loadWifiStations(a){return function(){var t=!0===a?"?refresh":"";$(".js-wifi-stations").addClass("loading-spinner").empty().load("/ajax/networking/wifi_stations.php"+t,function(){$(this).removeClass("loading-spinner")})}}$(document).on("click",".js-add-dhcp-static-lease",function(t){t.preventDefault();var a=$(".js-new-dhcp-static-lease"),e=$("input[name=mac]",a).val().trim(),n=$("input[name=ip]",a).val().trim();if(""!=e&&""!=n){var i=$("#js-dhcp-static-lease-row").html().replace("{{ mac }}",e).replace("{{ ip }}",n);$(".js-dhcp-static-lease-container").append(i),$("input[name=mac]",a).val(""),$("input[name=ip]",a).val("")}}),$(document).on("click",".js-remove-dhcp-static-lease",function(t){t.preventDefault(),$(this).parents(".js-dhcp-static-lease-row").remove()}),$(document).on("submit",".js-dhcp-settings-form",function(t){$(".js-add-dhcp-static-lease").trigger("click")}),$(".js-reload-wifi-stations").on("click",loadWifiStations(!0)),$(document).on("click",".js-toggle-password",function(t){var a=$(t.target),e=$(a.data("target"));e.is(":input")&&(t.preventDefault(),a.data("__toggle-with-initial")||a.data("__toggle-with-initial",a.text()),"password"===e.attr("type")?(a.text(a.data("toggle-with")),e.attr("type","text")):(a.text(a.data("__toggle-with-initial")),e.attr("type","password")))}),$(document).on("keyup",".js-validate-psk",function(t){var a=$(t.target),e=a.data("colors").split(","),n=$(a.data("target"));a.val().length<8||63<a.val().length?(a.css("backgroundColor",e[0]),n.attr("disabled",!0)):(a.css("backgroundColor",e[1]),n.attr("disabled",!1))}),$(document).ajaxSend(setCSRFTokenHeader).ready(contentLoaded).ready(loadWifiStations());

View File

@@ -1,88 +0,0 @@
// Link quality gauge for ChartJS
// Support for dark theme
theme = getCookie('theme');
if (theme == 'lightsout.css') {
var borderColor = 'rgba(37, 153, 63, 1)';
var labelColor = 'rgba(37, 153, 63, 1)';
} else if (theme == 'material-light.php') {
var borderColor = '#f2f2fb';
var labelColor = '#f2f2fb';
} else if (theme == 'material-dark.php') {
var borderColor = '#f2f2fb';
var labelColor = '#f2f2fb';
} else {
var borderColor = 'rgba(147, 210, 162, 1)';
var labelColor = 'rgba(130, 130, 130, 1)';
}
let data1 = {
datasets: [{
data: [linkQ, 100-linkQ],
backgroundColor: 'transparent',
borderColor: borderColor,
}],
};
let config = {
type: 'doughnut',
data: data1,
options: {
aspectRatio: 2,
responsive: true,
maintainAspectRatio: false,
tooltips: {enabled: false},
hover: {mode: null},
legend: {
display: false,
},
rotation: (2/3)*Math.PI,//2+(1/3),
circumference: (1+(2/3)) * Math.PI, // * Math.PI,
cutoutPercentage: 80,
animation: {
animateScale: false,
animateRotate: true
},
tooltips: {
enabled: false
}
},
centerText: {
display: true,
text: linkQ + "%"
},
plugins: [{
beforeDraw: function(chart) {
if (chart.config.centerText.display !== null &&
typeof chart.config.centerText.display !== 'undefined' &&
chart.config.centerText.display) {
drawLinkQ(chart);
}
}
}]
};
function drawLinkQ(chart) {
let width = chart.chart.width;
let height = chart.chart.height;
let ctx = chart.chart.ctx;
ctx.restore();
let fontSize = (height / 100).toFixed(2);
ctx.font = fontSize + "em sans-serif";
ctx.fillStyle = labelColor;
ctx.textBaseline = "middle";
let text = chart.config.centerText.text;
let textX = Math.round((width - ctx.measureText(text).width) * 0.5);
let textY = height / 2;
ctx.fillText(text, textX, textY);
ctx.save();
}
window.onload = function() {
let ctx = document.getElementById("divChartLinkQ").getContext("2d");
var chart = new Chart(ctx, config);
};

594
app/js/ui/main.js Normal file
View File

@@ -0,0 +1,594 @@
function msgShow(retcode,msg) {
if(retcode == 0) { var alertType = 'success';
} else if(retcode == 2 || retcode == 1) {
var alertType = 'danger';
}
var htmlMsg = '<div class="alert alert-'+alertType+' alert-dismissible" role="alert"><button type="button" class="btn-close" data-dismiss="alert" data-bs-dismiss="alert" aria-label="Close"></button>'+msg+'</div>';
return htmlMsg;
}
function createNetmaskAddr(bitCount) {
var mask=[];
for(i=0;i<4;i++) {
var n = Math.min(bitCount, 8);
mask.push(256 - Math.pow(2, 8-n));
bitCount -= n;
}
return mask.join('.');
}
function setupTabs() {
$('a[data-bs-toggle="tab"]').on('shown.bs.tab',function(e){
var target = $(e.target).attr('href');
if(!target.match('summary')) {
var int = target.replace("#","");
}
});
}
$(document).on("click", ".js-add-dhcp-static-lease", function(e) {
e.preventDefault();
var container = $(".js-new-dhcp-static-lease");
var mac = $("input[name=mac]", container).val().trim();
var ip = $("input[name=ip]", container).val().trim();
var comment = $("input[name=comment]", container).val().trim();
if (mac == "" || ip == "") {
return;
}
var row = $("#js-dhcp-static-lease-row").html()
.replace("{{ mac }}", mac)
.replace("{{ ip }}", ip)
.replace("{{ comment }}", comment);
$(".js-dhcp-static-lease-container").append(row);
$("input[name=mac]", container).val("");
$("input[name=ip]", container).val("");
$("input[name=comment]", container).val("");
});
$(document).on("click", ".js-remove-dhcp-static-lease", function(e) {
e.preventDefault();
$(this).parents(".js-dhcp-static-lease-row").remove();
});
$(document).on("submit", ".js-dhcp-settings-form", function(e) {
$(".js-add-dhcp-static-lease").trigger("click");
});
$(document).on("click", ".js-add-dhcp-upstream-server", function(e) {
e.preventDefault();
var field = $("#add-dhcp-upstream-server-field")
var row = $("#dhcp-upstream-server").html().replace("{{ server }}", field.val())
if (field.val().trim() == "") { return }
$(".js-dhcp-upstream-servers").append(row)
field.val("")
});
$(document).on("click", ".js-remove-dhcp-upstream-server", function(e) {
e.preventDefault();
$(this).parents(".js-dhcp-upstream-server").remove();
});
$(document).on("submit", ".js-dhcp-settings-form", function(e) {
$(".js-add-dhcp-upstream-server").trigger("click");
});
/**
* mark a form field, e.g. a select box, with the class `.js-field-preset`
* and give it an attribute `data-field-preset-target` with a text field's
* css selector.
*
* now, if the element marked `.js-field-preset` receives a `change` event,
* its value will be copied to all elements matching the selector in
* data-field-preset-target.
*/
$(document).on("change", ".js-field-preset", function(e) {
var selector = this.getAttribute("data-field-preset-target")
var value = "" + this.value
var syncValue = function(el) { el.value = value }
if (value.trim() === "") { return }
document.querySelectorAll(selector).forEach(syncValue)
});
$(document).on("click", "#gen_wpa_passphrase", function(e) {
$('#txtwpapassphrase').val(genPassword(63));
});
$(document).on("click", "#gen_apikey", function(e) {
$('#txtapikey').val(genPassword(32).toLowerCase());
});
// Enable Bootstrap tooltips
$(function () {
$('[data-bs-toggle="tooltip"]').tooltip()
})
function genPassword(pwdLen) {
var pwdChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var rndPass = Array(pwdLen).fill(pwdChars).map(function(x) { return x[Math.floor(Math.random() * x.length)] }).join('');
return rndPass;
}
function setupBtns() {
$('#btnSummaryRefresh').click(function(){getAllInterfaces();});
$('.intsave').click(function(){
var int = $(this).data('int');
saveNetworkSettings(int);
});
$('.intapply').click(function(){
applyNetworkSettings();
});
}
function setCSRFTokenHeader(event, xhr, settings) {
var csrfToken = $('meta[name=csrf_token]').attr('content');
if (/^(POST|PATCH|PUT|DELETE)$/i.test(settings.type)) {
xhr.setRequestHeader("X-CSRF-Token", csrfToken);
}
}
function contentLoaded() {
pageCurrent = window.location.href.split("/").pop();
switch(pageCurrent) {
case "network_conf":
getAllInterfaces();
setupTabs();
setupBtns();
break;
case "hostapd_conf":
getChannel();
setHardwareModeTooltip();
break;
case "dhcpd_conf":
loadInterfaceDHCPSelect();
break;
}
}
function setDHCPToggles(state) {
if ($('#chkfallback').is(':checked') && state) {
$('#chkfallback').prop('checked', state);
}
if ($('#dhcp-iface').is(':checked') && !state) {
$('#dhcp-iface').prop('checked', state);
setDhcpFieldsDisabled();
}
$('#chkfallback').prop('disabled', state);
$('#dhcp-iface').prop('disabled', !state);
}
$('#chkfallback').change(function() {
if ($('#chkfallback').is(':checked')) {
setStaticFieldsEnabled();
} else {
setStaticFieldsDisabled();
}
});
$('#performupdateModal').on('shown.bs.modal', function (e) {
fetchUpdateResponse();
});
$('#hostapdModal').on('shown.bs.modal', function (e) {
var seconds = 3;
var pct = 0;
var countDown = setInterval(function(){
if(seconds <= 0){
clearInterval(countDown);
}
document.getElementsByClassName('progress-bar').item(0).setAttribute('style','width:'+Number(pct)+'%');
seconds --;
pct = Math.floor(100-(seconds*100/4));
}, 500);
});
$('#configureClientModal').on('shown.bs.modal', function (e) {
});
$('#ovpn-confirm-activate').on('shown.bs.modal', function (e) {
var data = $(e.relatedTarget).data();
$('.btn-activate', this).data('recordId', data.recordId);
});
$('#ovpn-userpw,#ovpn-certs').on('click', function (e) {
if (this.id == 'ovpn-userpw') {
$('#PanelCerts').hide();
$('#PanelUserPW').show();
} else if (this.id == 'ovpn-certs') {
$('#PanelUserPW').hide();
$('#PanelCerts').show();
}
});
$('#install-user-plugin').on('shown.bs.modal', function (e) {
var button = $(e.relatedTarget);
$(this).data('button', button);
var manifestData = button.data('plugin-manifest');
var installed = button.data('plugin-installed') || false;
var repoPublic = button.data('repo-public') || false;
var installPath = manifestData.install_path;
if (!installed && repoPublic && installPath === 'plugins-available') {
insidersHTML = 'Available with <i class="fas fa-heart heart me-1"></i><a href="https://docs.raspap.com/insiders" target="_blank" rel="noopener">Insiders</a>';
$('#plugin-additional').html(insidersHTML);
} else {
$('#plugin-additional').empty();
}
if (manifestData) {
$('#plugin-docs').html(manifestData.plugin_docs
? `<a href="${manifestData.plugin_docs}" target="_blank">${manifestData.plugin_docs}</a>`
: 'Unknown');
$('#plugin-icon').attr('class', `${manifestData.icon || 'fas fa-plug'} link-secondary h5 me-2`);
$('#plugin-name').text(manifestData.name || 'Unknown');
$('#plugin-version').text(manifestData.version || 'Unknown');
$('#plugin-description').text(manifestData.description || 'No description provided');
$('#plugin-author').html(manifestData.author
? manifestData.author + (manifestData.author_uri
? ` (<a href="${manifestData.author_uri}" target="_blank">profile</a>)` : '') : 'Unknown');
$('#plugin-license').text(manifestData.license || 'Unknown');
$('#plugin-locale').text(manifestData.default_locale || 'Unknown');
$('#plugin-configuration').html(formatProperty(manifestData.configuration || 'None'));
$('#plugin-packages').html(formatProperty(manifestData.keys || 'None'));
$('#plugin-dependencies').html(formatProperty(manifestData.dependencies || 'None'));
$('#plugin-javascript').html(formatProperty(manifestData.javascript || 'None'));
$('#plugin-sudoers').html(formatProperty(manifestData.sudoers || 'None'));
$('#plugin-user-name').html((manifestData.user_nonprivileged && manifestData.user_nonprivileged.name) || 'None');
}
if (installed) {
$('#js-install-plugin-confirm').html('OK');
} else if (!installed && repoPublic && installPath == 'plugins-available') {
$('#js-install-plugin-confirm').html('Get Insiders');
} else {
$('#js-install-plugin-confirm').html('Install now');
}
});
$('#js-install-plugin-ok').on('click', function (e) {
$("#install-plugin-progress").modal('hide');
window.location.reload();
});
function formatProperty(prop) {
if (Array.isArray(prop)) {
if (typeof prop[0] === 'object') {
return prop.map(item => {
return Object.entries(item)
.map(([key, value]) => `${key}: ${value}`)
.join('<br/>');
}).join('<br/>');
}
return prop.map(line => `${line}<br/>`).join('');
}
if (typeof prop === 'object') {
return Object.entries(prop)
.map(([key, value]) => `${key}: ${value}`)
.join('<br/>');
}
return prop || 'None';
}
$(document).ready(function(){
$("#PanelManual").hide();
$('.ip_address').mask('0ZZ.0ZZ.0ZZ.0ZZ', {
translation: {
'Z': {
pattern: /[0-9]/, optional: true
}
},
placeholder: "___.___.___.___"
});
$('.mac_address').mask('FF:FF:FF:FF:FF:FF', {
translation: {
'F': {
pattern: /[0-9a-fA-F]/, optional: false
}
},
placeholder: "__:__:__:__:__:__"
});
});
$(document).ready(function() {
$('.cidr').mask('099.099.099.099/099', {
translation: {
'0': { pattern: /[0-9]/ }
},
placeholder: "___.___.___.___/___"
});
});
$('#wg-upload,#wg-manual').on('click', function (e) {
if (this.id == 'wg-upload') {
$('#PanelManual').hide();
$('#PanelUpload').show();
} else if (this.id == 'wg-manual') {
$('#PanelUpload').hide();
$('#PanelManual').show();
}
});
$(".custom-file-input").on("change", function() {
var fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
// Event listener for Bootstrap's form validation
window.addEventListener('load', function() {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
var validation = Array.prototype.filter.call(forms, function(form) {
form.addEventListener('submit', function(event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
}, false);
function showSessionExpiredModal() {
$('#sessionTimeoutModal').modal('show');
}
$(document).on("click", "#js-session-expired-login", function(e) {
const loginModal = $('#modal-admin-login');
const redirectUrl = window.location.pathname;
window.location.href = `/login?action=${encodeURIComponent(redirectUrl)}`;
});
// show modal login on page load
$(document).ready(function () {
const params = new URLSearchParams(window.location.search);
const redirectUrl = $('#redirect-url').val() || params.get('action') || '/';
$('#modal-admin-login').modal('show');
$('#redirect-url').val(redirectUrl);
$('#username').focus();
$('#username').addClass("focusedInput");
});
// DHCP or Static IP option group
$('#chkstatic').on('change', function() {
if (this.checked) {
setStaticFieldsEnabled();
}
});
$('#chkdhcp').on('change', function() {
this.checked ? setStaticFieldsDisabled() : null;
});
$('input[name="dhcp-iface"]').change(function() {
if ($('input[name="dhcp-iface"]:checked').val() == '1') {
setDhcpFieldsEnabled();
} else {
setDhcpFieldsDisabled();
}
});
function setStaticFieldsEnabled() {
$('#txtipaddress').prop('required', true);
$('#txtsubnetmask').prop('required', true);
$('#txtgateway').prop('required', true);
$('#txtipaddress').removeAttr('disabled');
$('#txtsubnetmask').removeAttr('disabled');
$('#txtgateway').removeAttr('disabled');
}
function setStaticFieldsDisabled() {
$('#txtipaddress').prop('disabled', true);
$('#txtsubnetmask').prop('disabled', true);
$('#txtgateway').prop('disabled', true);
$('#txtipaddress').removeAttr('required');
$('#txtsubnetmask').removeAttr('required');
$('#txtgateway').removeAttr('required');
}
function setDhcpFieldsEnabled() {
$('#txtrangestart').prop('required', true);
$('#txtrangeend').prop('required', true);
$('#txtrangeleasetime').prop('required', true);
$('#cbxrangeleasetimeunits').prop('required', true);
$('#txtrangestart').removeAttr('disabled');
$('#txtrangeend').removeAttr('disabled');
$('#txtrangeleasetime').removeAttr('disabled');
$('#cbxrangeleasetimeunits').removeAttr('disabled');
$('#txtdns1').removeAttr('disabled');
$('#txtdns2').removeAttr('disabled');
$('#txtmetric').removeAttr('disabled');
}
function setDhcpFieldsDisabled() {
$('#txtrangestart').removeAttr('required');
$('#txtrangeend').removeAttr('required');
$('#txtrangeleasetime').removeAttr('required');
$('#cbxrangeleasetimeunits').removeAttr('required');
$('#txtrangestart').prop('disabled', true);
$('#txtrangeend').prop('disabled', true);
$('#txtrangeleasetime').prop('disabled', true);
$('#cbxrangeleasetimeunits').prop('disabled', true);
$('#txtdns1').prop('disabled', true);
$('#txtdns2').prop('disabled', true);
$('#txtmetric').prop('disabled', true);
}
// Static Array method
Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);
$(document).on("click", ".js-toggle-password", function(e) {
var button = $(e.currentTarget);
var field = $(button.data("bsTarget"));
if (field.is(":input")) {
e.preventDefault();
if (!button.data("__toggle-with-initial")) {
$("i", button).removeClass("fas fa-eye").addClass(button.attr("data-toggle-with"));
}
if (field.attr("type") === "password") {
field.attr("type", "text");
} else {
$("i", button).removeClass("fas fa-eye-slash").addClass("fas fa-eye");
field.attr("type", "password");
}
}
});
$(function() {
$('#theme-select').change(function() {
var theme = themes[$( "#theme-select" ).val() ];
var hasDarkTheme = theme === 'custom.php';
var nightModeChecked = $("#night-mode").prop("checked");
if (nightModeChecked && hasDarkTheme) {
if (theme === "custom.php") {
set_theme("dark.css");
}
} else {
set_theme(theme);
}
});
});
function set_theme(theme) {
$('link[title="main"]').attr('href', 'app/css/' + theme);
// persist selected theme in cookie
setCookie('theme',theme,90);
}
$(function() {
var currentTheme = getCookie('theme');
// Check if the current theme is a dark theme
var isDarkTheme = currentTheme === 'dark.css';
$('#night-mode').prop('checked', isDarkTheme);
$('#night-mode').change(function() {
var state = $(this).is(':checked');
var currentTheme = getCookie('theme');
if (state == true) {
if (currentTheme == 'custom.php') {
set_theme('dark.css');
}
} else {
if (currentTheme == 'dark.css') {
set_theme('custom.php');
}
}
});
});
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
function getCookie(cname) {
var regx = new RegExp(cname + "=([^;]+)");
var value = regx.exec(document.cookie);
return (value != null) ? unescape(value[1]) : null;
}
// Define themes
var themes = {
"default": "custom.php",
"hackernews" : "hackernews.css"
}
// Adds active class to current nav-item
$(window).bind("load", function() {
var url = window.location;
$('.sb-nav-link-icon a').filter(function() {
return this.href == url;
}).parent().addClass('active');
});
// Sets focus on a specified tab
document.addEventListener("DOMContentLoaded", function () {
const params = new URLSearchParams(window.location.search);
const targetTab = params.get("tab");
if (targetTab) {
let tabElement = document.querySelector(`[data-bs-toggle="tab"][href="#${targetTab}"]`);
if (tabElement) {
let tab = new bootstrap.Tab(tabElement);
tab.show();
}
}
});
function disableValidation(form) {
form.removeAttribute("novalidate");
form.classList.remove("needs-validation");
form.querySelectorAll("[required]").forEach(function (field) {
field.removeAttribute("required");
});
}
function updateActivityLED() {
const threshold_bytes = 300;
fetch('app/net_activity')
.then(res => res.text())
.then(data => {
const activity = parseInt(data.trim());
const leds = document.querySelectorAll('.hostapd-led');
if (!isNaN(activity)) {
leds.forEach(led => {
if (activity > threshold_bytes) {
led.classList.add('led-pulse');
setTimeout(() => {
led.classList.remove('led-pulse');
}, 50);
} else {
led.classList.remove('led-pulse');
}
});
}
})
.catch(() => { /* ignore fetch errors */ });
}
setInterval(updateActivityLED, 100);
$(document).ready(function() {
const $htmlElement = $('html');
const $modeswitch = $('#night-mode');
$modeswitch.on('change', function() {
const isChecked = $(this).is(':checked');
const newTheme = isChecked ? 'dark' : 'light';
$htmlElement.attr('data-bs-theme', newTheme);
localStorage.setItem('bsTheme', newTheme);
});
});
$(document)
.ajaxSend(setCSRFTokenHeader)
.ready(contentLoaded)
.ready(loadWifiStations());
// To auto-close Bootstrap alerts; time is in milliseconds
const alertTimeout = parseInt(getCookie('alert_timeout'), 10);
if (!isNaN(alertTimeout) && alertTimeout > 0) {
window.setTimeout(function() {
$(".alert").fadeTo(500, 0).slideUp(500, function(){
$(this).remove();
});
}, alertTimeout);
}

189
app/js/vendor/speedtestUI.js vendored Normal file
View File

@@ -0,0 +1,189 @@
function I(i){return document.getElementById(i);}
const origin=window.location.origin;
const host=window.location.host;
var SPEEDTEST_SERVERS=[
{
"name":"RaspAP Speedtest server (US)",
"server":"https://speedtest.raspap.com/",
"dlURL":"backend/garbage.php",
"ulURL":"backend/empty.php",
"pingURL":"backend/empty.php",
"getIpURL":"backend/getIP.php"
},
{
"name":"RaspAP ("+host+")",
"server":origin,
"dlURL":"dist/speedtest/backend/garbage.php",
"ulURL":"dist/speedtest/backend/empty.php",
"pingURL":"dist/speedtest/backend/empty.php",
"getIpURL":"dist/speedtest/backend/getIP.php"
}
];
//INITIALIZE SPEEDTEST
var s=new Speedtest(); //create speedtest object
s.setParameter("telemetry_level","basic"); //enable telemetry
//SERVER AUTO SELECTION
function initServers(){
var noServersAvailable=function(){
I("message").innerHTML="No servers available";
}
var runServerSelect=function(){
s.selectServer(function(server){
if(server!=null){ //at least 1 server is available
I("loading").className="hidden"; //hide loading message
//populate server list for manual selection
for(var i=0;i<SPEEDTEST_SERVERS.length;i++){
//if(SPEEDTEST_SERVERS[i].pingT==-1) continue;
var option=document.createElement("option");
option.value=i;
option.textContent=SPEEDTEST_SERVERS[i].name;
if(SPEEDTEST_SERVERS[i]===server) option.selected=true;
I("server").appendChild(option);
}
//show test UI
I("testWrapper").className="visible";
initUI();
}else{ //no servers are available, the test cannot proceed
noServersAvailable();
}
});
}
if(typeof SPEEDTEST_SERVERS === "string"){
//need to fetch list of servers from specified URL
s.loadServerList(SPEEDTEST_SERVERS,function(servers){
if(servers==null){ //failed to load server list
noServersAvailable();
}else{ //server list loaded
SPEEDTEST_SERVERS=servers;
runServerSelect();
}
});
}else{
//hardcoded server list
s.addTestPoints(SPEEDTEST_SERVERS);
runServerSelect();
}
}
var meterBk=/Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent)?"#EAEAEA":"#80808040";
var dlColor="#4BC0C0",
ulColor="#616161";
var progColor=meterBk;
//CODE FOR GAUGES
function drawMeter(c,amount,bk,fg,progress,prog){
var ctx=c.getContext("2d");
var dp=window.devicePixelRatio||1;
var cw=c.clientWidth*dp, ch=c.clientHeight*dp;
var sizScale=ch*0.0055;
if(c.width==cw&&c.height==ch){
ctx.clearRect(0,0,cw,ch);
}else{
c.width=cw;
c.height=ch;
}
ctx.beginPath();
ctx.strokeStyle=bk;
ctx.lineWidth=12*sizScale;
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,Math.PI*0.1);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle=fg;
ctx.lineWidth=12*sizScale;
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,amount*Math.PI*1.2-Math.PI*1.1);
ctx.stroke();
if(typeof progress !== "undefined"){
ctx.fillStyle=prog;
ctx.fillRect(c.width*0.3,c.height-16*sizScale,c.width*0.4*progress,4*sizScale);
}
}
function mbpsToAmount(s){
return 1-(1/(Math.pow(1.3,Math.sqrt(s))));
}
function format(d){
d=Number(d);
if(d<10) return d.toFixed(2);
if(d<100) return d.toFixed(1);
return d.toFixed(0);
}
//UI CODE
var uiData=null;
function startStop(){
if(s.getState()==3){
//speedtest is running, abort
s.abort();
data=null;
I("startStopBtn").className="btn btn-outline btn-primary";
I("server").disabled=false;
initUI();
}else{
//test is not running, begin
I("startStopBtn").className="btn btn-outline btn-primary running";
I("server").disabled=true;
s.onupdate=function(data){
uiData=data;
};
s.onend=function(aborted){
I("startStopBtn").className="btn btn-outline btn-primary";
I("server").disabled=false;
updateUI(true);
if(!aborted){
//if testId is present, show sharing panel, otherwise do nothing
try{
var testId=uiData.testId;
if(testId!=null){
var shareURL=window.location.href.substring(0,window.location.href.lastIndexOf("/"))+"/results/?id="+testId;
I("resultsImg").src=shareURL;
I("resultsURL").value=shareURL;
I("testId").innerHTML=testId;
I("shareArea").style.display="";
}
}catch(e){}
}
};
s.start();
}
}
//this function reads the data sent back by the test and updates the UI
function updateUI(forced){
if(!forced&&s.getState()!=3) return;
if(uiData==null) return;
var status=uiData.testState;
I("ip").textContent=uiData.clientIp;
I("dlText").textContent=(status==1&&uiData.dlStatus==0)?"...":format(uiData.dlStatus);
drawMeter(I("dlMeter"),mbpsToAmount(Number(uiData.dlStatus*(status==1?oscillate():1))),meterBk,dlColor,Number(uiData.dlProgress),progColor);
I("ulText").textContent=(status==3&&uiData.ulStatus==0)?"...":format(uiData.ulStatus);
drawMeter(I("ulMeter"),mbpsToAmount(Number(uiData.ulStatus*(status==3?oscillate():1))),meterBk,ulColor,Number(uiData.ulProgress),progColor);
I("pingText").textContent=format(uiData.pingStatus);
I("jitText").textContent=format(uiData.jitterStatus);
}
function oscillate(){
return 1+0.02*Math.sin(Date.now()/100);
}
//update the UI every frame
window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||(function(callback,element){setTimeout(callback,1000/60);});
function frame(){
requestAnimationFrame(frame);
updateUI();
}
frame(); //start frame loop
//function to (re)initialize UI
function initUI(){
drawMeter(I("dlMeter"),0,meterBk,dlColor,0);
drawMeter(I("ulMeter"),0,meterBk,ulColor,0);
I("dlText").textContent="";
I("ulText").textContent="";
I("pingText").textContent="";
I("jitText").textContent="";
I("ip").textContent="";
}
// add EventListener to diagnostic tab
var e = document.querySelectorAll("a[href^='#diagnostic']");
e[0].addEventListener("click", function(){
initServers()
});

78
app/lib/signprint.php Normal file
View File

@@ -0,0 +1,78 @@
<?php
require_once '../../includes/config.php';
require_once '../../includes/defaults.php';
// prevent direct file access
if (!isset($_SERVER['HTTP_REFERER'])) {
header('HTTP/1.0 403 Forbidden');
exit;
}
$hostapd = parse_ini_file(RASPI_HOSTAPD_CONFIG, false, INI_SCANNER_RAW);
$ssid = $hostapd['ssid'];
$password = isset($hostapd['wpa_psk']) ? $hostapd['wpa_psk'] : $hostapd['wpa_passphrase'];
?>
<html>
<head>
<title><?php echo _("Printable Wi-Fi sign"); ?></title>
<!-- Bootstrap Core CSS -->
<link href="../../dist/bootstrap/css/bootstrap.css" rel="stylesheet">
<!-- SB-Admin-2 CSS -->
<link href="../../dist/sb-admin-2/css/sb-admin-2.min.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="../../dist/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
</head>
<body id="page-top">
<div id="wrapper">
<div id="content-wrapper" class="d-flex flex-column">
<div id="container">
<div class="row text-center">
<div class="col">
<div class="mt-5 mb-5">
<h2><i class="fas fa-wifi"></i> <?php echo _("Wi-Fi Connect"); ?></h2>
</div>
</div>
</div><!-- /row -->
<div class="row">
<div class="col"></div>
<div class="col-5">
<img src="../img/wifi-qr-code.php" class="figure-img img-fluid" alt="RaspAP Wifi QR code" style="width:100%;">
</div>
<div class="col"></div>
</div><!-- /row -->
<div class="row text-center">
<div class="col"></div>
<div class="col-8">
<div class="mt-4">
<div><?php echo _("To connect with your phone or tablet, scan the QR code above with your camera app."); ?></div>
<div><?php echo _("For other devices, use the login credentials below."); ?></div>
</div>
</div>
<div class="col"></div>
</div><!-- /row -->
<div class="row text-center">
<div class="col"></div>
<div class="col-8">
<div class="mt-4">
<?php echo _("Network"); ?>
<h3 class="mb-3"><?php echo $ssid ?></h3>
<?php echo _("Password"); ?>
<h3 class="mb-5"><?php echo $password ?></h3>
</div>
</div>
<div class="col"></div>
</div><!-- /row -->
</div><!-- /content -->
</div><!-- /content-wrapper -->
</div><!-- /page wrapper -->
</body>
</html>

View File

@@ -1,17 +1,45 @@
{
"StevenBlack/hosts": [
"StevenBlack/hosts (default)"
],
"badmojr/hosts": [
"badmojr/1Hosts (Mini)",
"badmojr/1Hosts (Lite)",
"badmojr/1Hosts (Pro)",
"badmojr/1Hosts (Xtra)"
],
"OISD/domains": [
"oisd/big (default)",
"oisd/small",
"oisd/nsfw"
]
"StevenBlack/hosts": {
"StevenBlack/hosts (default)": {
"list_url": "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts",
"dest_file": "hostnames.txt"
}
},
"HaGeZi/hosts": {
"hagezi/hosts (Light)": {
"list_url": "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/light.txt",
"dest_file": "hostnames.txt"
},
"hagezi/hosts (Normal)": {
"list_url": "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/multi.txt",
"dest_file": "hostnames.txt"
},
"hagezi/hosts (Pro)": {
"list_url": "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/pro.txt",
"dest_file": "hostnames.txt"
},
"hagezi/hosts (Pro++)": {
"list_url": "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/pro.plus.txt",
"dest_file": "hostnames.txt"
},
"hagezi/hosts (Ultimate)": {
"list_url": "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/ultimate.txt",
"dest_file": "hostnames.txt"
}
},
"OISD/domains": {
"oisd/big (default)": {
"list_url": "https://big.oisd.nl/dnsmasq",
"dest_file": "domains.txt"
},
"oisd/small": {
"list_url": "https://small.oisd.nl/dnsmasq",
"dest_file": "domains.txt"
},
"oisd/nsfw": {
"list_url": "https://nsfw.oisd.nl/dnsmasq",
"dest_file": "domains.txt"
}
}
}

View File

@@ -32,6 +32,7 @@ define('RASPI_OPENVPN_CLIENT_CONFIG', '/etc/openvpn/client/client.conf');
define('RASPI_OPENVPN_CLIENT_LOGIN', '/etc/openvpn/client/login.conf');
define('RASPI_WIREGUARD_PATH', '/etc/wireguard/');
define('RASPI_WIREGUARD_CONFIG', RASPI_WIREGUARD_PATH.'wg0.conf');
define('RASPI_IPTABLES_CONF', RASPI_CONFIG.'/networking/iptables_rules.json');
define('RASPI_TORPROXY_CONFIG', '/etc/tor/torrc');
define('RASPI_LIGHTTPD_CONFIG', '/etc/lighttpd/lighttpd.conf');
define('RASPI_ACCESS_CHECK_IP', '1.1.1.1');
@@ -40,6 +41,10 @@ define('RASPI_ACCESS_CHECK_DNS', 'one.one.one.one');
// Constant for the GitHub API latest release endpoint
define('RASPI_API_ENDPOINT', 'https://api.github.com/repos/RaspAP/raspap-webgui/releases/latest');
// Captive portal detection - returns 204 or 200 is successful
define('RASPI_ACCESS_CHECK_URL', 'http://detectportal.firefox.com');
define('RASPI_ACCESS_CHECK_URL_CODE', 200);
// Constant for the 5GHz wireless regulatory domain
define("RASPI_5GHZ_CHANNEL_MIN", 100);
define("RASPI_5GHZ_CHANNEL_MAX", 192);

View File

@@ -1,66 +1,114 @@
{
"dhcp": {
"wlan0": {
"static ip_address": [ "10.3.141.1/24" ],
"static routers": [ "10.3.141.1" ],
"static domain_name_server": [ "1.1.1.1 8.8.8.8" ],
"subnetmask": [ "255.255.255.0" ]
"hostapd":{
"modes":{
"n":{
"settings":[
"hw_mode=g",
"ieee80211n=1",
"wmm_enabled=1"
]
},
"ac":{
"settings":[
"hw_mode=a",
"# N",
"ieee80211n=1",
"require_ht=1",
"ht_capab=[MAX-AMSDU-3839][HT40+][SHORT-GI-20][SHORT-GI-40][DSSS_CCK-40]",
"# AC",
"ieee80211ac=1",
"require_vht=1",
"ieee80211d=0",
"ieee80211h=0",
"vht_capab=[MAX-AMSDU-3839][SHORT-GI-80]",
"vht_oper_chwidth=1",
"vht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}"
]
},
"g":{
"settings":[
"hw_mode=g",
"ieee80211n=0"
]
},
"a":{
"settings":[
"hw_mode=a",
"ieee80211n=0"
]
},
"b":{
"settings":[
"hw_mode=b",
"ieee80211n=0"
]
}
},
"wlan1": {
"static ip_address": [ "10.9.141.1/24" ],
"static routers": [ "10.9.141.1" ],
"static domain_name_server": [ "1.1.1.1 8.8.8.8" ],
"subnetmask": [ "255.255.255.0" ]
"dhcp":{
"wlan0":{
"static ip_address":[ "10.3.141.1/24" ],
"static routers":[ "10.3.141.1" ],
"static domain_name_server":[ "1.1.1.1 8.8.8.8" ],
"subnetmask":[ "255.255.255.0" ]
},
"wlan1":{
"static ip_address":[ "10.9.141.1/24" ],
"static routers":[ "10.9.141.1" ],
"static domain_name_server":[ "1.1.1.1 8.8.8.8" ],
"subnetmask":[ "255.255.255.0" ]
},
"uap0":{
"static ip_address":[ "192.168.50.1/24" ],
"static routers":[ "192.168.50.1" ],
"static domain_name_server":[ "1.1.1.1 8.8.8.8" ],
"subnetmask":[ "255.255.255.0" ]
},
"options":{
"# RaspAP default configuration":null,
"hostname":null,
"clientid":null,
"persistent":null,
"option rapid_commit":null,
"option domain_name_servers, domain_name, domain_search, host_name":null,
"option classless_static_routes":null,
"option ntp_servers":null,
"require dhcp_server_identifier":null,
"slaac private":null,
"nohook lookup-hostname":null
}
},
"uap0": {
"static ip_address": [ "192.168.50.1/24" ],
"static routers": [ "192.168.50.1" ],
"static domain_name_server": [ "1.1.1.1 8.8.8.8" ],
"subnetmask": [ "255.255.255.0" ]
"dnsmasq":{
"wlan0":{
"dhcp-range":[ "10.3.141.50,10.3.141.254,255.255.255.0,12h" ]
},
"wlan1":{
"dhcp-range":[ "10.9.141.50,10.9.141.254,255.255.255.0,12h" ]
},
"uap0":{
"dhcp-range":[ "192.168.50.50,192.168.50.150,12h" ]
}
},
"options": {
"# RaspAP default configuration": null,
"hostname": null,
"clientid": null,
"persistent": null,
"option rapid_commit": null,
"option domain_name_servers, domain_name, domain_search, host_name": null,
"option classless_static_routes": null,
"option ntp_servers": null,
"require dhcp_server_identifier": null,
"slaac private": null,
"nohook lookup-hostname": null
}
},
"dnsmasq": {
"wlan0": {
"dhcp-range": [ "10.3.141.50,10.3.141.254,255.255.255.0,12h" ]
},
"wlan1": {
"dhcp-range": [ "10.9.141.50,10.9.141.254,255.255.255.0,12h" ]
},
"uap0": {
"dhcp-range": [ "192.168.50.50,192.168.50.150,12h" ]
}
},
"wireguard": {
"server": {
"Address": [ "10.8.2.1/24" ],
"ListenPort": [ "51820" ],
"DNS": [ "9.9.9.9" ],
"PostUp": [ "iptables -A FORWARD -i wlan0 -o wg0 -j ACCEPT; iptables -A FORWARD -i wg0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE" ],
"PostDown": [ "iptables -D FORWARD -i wlan0 -o wg0 -j ACCEPT; iptables -D FORWARD -i wg0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE" ]
},
"peer": {
"Address": [ "10.8.1.2/24" ],
"Endpoint": [ "10.8.2.1:51820" ],
"ListenPort": [ "21841" ],
"AllowedIPs": ["10.8.2.0/24"],
"PersistentKeepalive": [ "15" ]
}
},
"txpower": {
"dbm": [ "auto", "30", "20", "17", "10", "6", "3", "1", "0" ]
"wireguard":{
"server":{
"Address":[ "10.8.2.1/24" ],
"ListenPort":[ "51820" ],
"DNS":[ "9.9.9.9" ],
"PostUp":[ "iptables -A FORWARD -i wlan0 -o wg0 -j ACCEPT; iptables -A FORWARD -i wg0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE" ],
"PostDown":[ "iptables -D FORWARD -i wlan0 -o wg0 -j ACCEPT; iptables -D FORWARD -i wg0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE" ],
"PostUpEx":[ "iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL ! -d %s -j REJECT" ],
"PreDown":[ "iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL ! -d %s -j REJECT" ]
},
"peer":{
"Address":[ "10.8.1.2/24" ],
"Endpoint":[ "10.8.2.1:51820" ],
"ListenPort":[ "21841" ],
"AllowedIPs":[ "10.8.2.0/24" ],
"PersistentKeepalive":[ "15" ]
}
},
"txpower": {
"dbm": [ "auto", "30", "20", "17", "10", "6", "3", "1", "0" ]
}
}
}

View File

@@ -4,7 +4,7 @@ ctrl_interface_group=0
beacon_int=100
auth_algs=1
wpa_key_mgmt=WPA-PSK
ssid=raspi-webgui
ssid=RaspAP
channel=1
hw_mode=g
wpa_passphrase=ChangeMe

View File

@@ -1,3 +0,0 @@
files:
- source: /locale/en_US/LC_MESSAGES/messages.po
translation: /locale/%locale_with_underscore%/LC_MESSAGES/%original_file_name%

Binary file not shown.

View File

@@ -9,4 +9,6 @@
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="wireguard" d="M1023.147 463.147c0 0 23.595 496.853-522.453 496.853-482.859 0-497.963-476.587-497.963-476.587s-70.997-547.413 509.141-547.413c556.501 0 511.275 527.147 511.275 527.147zM347.947 636.757c102.4 62.72 233.344 24.363 282.368-69.888 9.301-17.877 10.496-45.355 4.608-64.128-20.352-64.683-68.309-100.949-134.187-116.395 19.413 16.64 34.859 35.499 39.808 61.525 1.195 5.504 1.88 11.827 1.88 18.31 0 20.027-6.533 38.528-17.584 53.488l0.174-0.246c-16.797 22.874-43.588 37.556-73.809 37.556-11.257 0-22.038-2.037-31.995-5.763l0.63 0.207c-40.533-15.36-62.72-52.395-58.752-97.877 3.712-42.24 35.797-69.632 95.787-80.043-8.96-4.736-15.872-8.235-22.613-11.989-27.988-15.524-51.374-35.995-69.74-60.451l-0.404-0.562c-6.101-8.192-10.24-8.875-19.541-3.2-120.619 73.771-128.384 258.859 3.371 339.456zM257.707 180.992c-19.413-4.949-38.187-12.203-57.984-18.688 9.685 65.365 86.229 125.568 150.997 118.699-18.043-24.598-29.583-54.982-31.551-87.945l-0.022-0.46c-21.504-3.968-41.813-6.613-61.44-11.605zM669.995 819.2c19.115-0.725 38.315-0.427 57.472-0.853 5.287-0.363 10.162-1.075 14.91-2.128l-0.659 0.123c-4.574-6.938-9.348-12.986-14.582-18.599l0.076 0.082c-6.827-6.4-14.549-12.629-24.448-2.944-2.347 2.347-7.979 1.792-12.075 1.877-19.072 0.213-38.144 0.853-57.173 0.128-17.856-0.589-34.82-2.396-51.386-5.353l2.149 0.318c-3.072-0.555-7.595-10.667-6.229-14.421 3.328-8.832 8.149-18.56 15.317-24.192 26.411-20.907 54.485-39.595 81.067-60.288 25.771-20.139 49.792-42.24 64.427-72.533 19.029-39.595 19.627-81.067 11.392-122.752-13.739-69.547-48.939-127.147-105.941-169.045-22.955-16.853-51.413-26.453-77.696-38.528-23.168-10.667-46.933-19.84-70.144-30.379-41.813-19.029-65.28-64.427-58.411-111.573 6.357-43.307 44.373-79.445 87.851-86.912 52.181-8.96 106.069 25.003 118.827 78.080 14.336 59.605-18.048 112.896-78.72 129.024l-10.923 2.816c16.213 7.253 30.208 12.416 43.179 19.541q33.835 18.645 66.475 39.467c6.4 4.096 9.856 4.096 15.36-0.597 41.685-36.096 66.56-80.981 73.557-135.979 11.52-91.093-31.573-174.763-112.896-217.643-125.781-66.347-279.765 9.173-307.541 148.651-23.808 119.467 60.501 227.84 162.005 248.747 43.648 9.003 83.541 27.179 114.56 60.8 20.053 21.675 29.739 40.277 33.067 48.683 5.86 14.568 9.259 31.458 9.259 49.142 0 0.094 0 0.187 0 0.281v-0.014c-0.72 15.473-4.371 29.921-10.408 43.044l0.296-0.719c-10.581 24.149-51.2 62.549-61.227 70.656l-95.573 74.837c-3.371 2.773-7.168 2.56-15.36 2.005-9.813-0.683-34.773-2.048-45.525 0.768 8.704 6.613 32.427 16.213 42.667 23.893-30.976 20.907-66.304 13.397-98.773 19.627 7.509 13.995 44.629 35.456 65.749 37.888-1.455 13.545-3.483 25.484-6.166 37.173l0.406-2.101c-1.28 4.736-6.571 9.387-11.221 12.075-11.179 6.571-23.083 11.989-35.968 18.517 10.935 7.156 24.244 11.558 38.555 11.945l0.101 0.002c1.66 0.068 3.608 0.107 5.566 0.107 11.77 0 23.21-1.408 34.163-4.064l-0.987 0.202c23.040-5.248 41.387-1.792 59.691 13.824-14.421 5.803-28.843 11.093-42.795 17.365-16.163 7.396-29.343 14.415-42.082 22.091l1.89-1.056c36.267-5.035 71.296-18.645 108.373-13.653l0.939 5.035-86.101 20.053c51.328 4.693 99.115 5.461 144.384-16.555 12.757-6.229 26.027-11.349 38.272-18.432 5.973-3.413 9.941-10.24 14.848-15.573 3.84-4.181 6.997-9.813 11.776-12.373 18.091-9.6 37.973-9.984 58.283-9.515l0.427 6.827c20.437-6.4 43.392-29.952 43.392-47.147-33.109 0-66.133 0.128-99.2-0.171-3.541 0-7.040-2.603-10.539-4.011 3.328-1.963 6.613-5.461 10.027-5.589zM627.328 868.139c-1.461-0.899-2.42-2.488-2.42-4.302 0-1.516 0.67-2.876 1.731-3.799l0.006-0.005c1.344-2.305 3.804-3.83 6.62-3.83 1.429 0 2.767 0.393 3.91 1.076l-0.035-0.019c3.2 1.621 6.315 3.328 10.155 5.333-3.072 2.645-5.547 4.864-8.107 6.955-4.523 3.712-8.235 1.365-11.861-1.408z" />
<glyph unicode="&#xe901;" glyph-name="raspap" horiz-adv-x="1031" d="M540.058 281.983c0-104.182-84.446-188.637-188.625-188.637-104.176 0-188.62 84.455-188.62 188.637 0 104.171 84.444 188.625 188.62 188.625 104.179 0 188.625-84.455 188.625-188.625zM351.437 550.062c-147.818 0-268.074-120.259-268.074-268.080 0-147.826 120.257-268.091 268.074-268.091s268.077 120.265 268.077 268.091c0 147.821-120.259 268.080-268.077 268.080zM351.437-58.985c-188 0-340.95 152.958-340.95 340.967 0 188.003 152.95 340.956 340.95 340.956 188.003 0 340.953-152.953 340.953-340.956 0-188.009-152.95-340.967-340.953-340.967zM404.82 698.222c185.52 0 339.484-137.497 365.479-315.929l79.208-5.253c-24.125 224.046-214.339 399.077-444.686 399.077-10.909 0-21.723-0.412-32.433-1.186l5.16-77.823c9.017 0.661 18.093 1.113 27.272 1.113zM404.989 874.303c285.73 0 520.41-222.659 539.731-503.584l78.375-5.205c-16.843 326.355-287.644 586.685-618.106 586.685-14.884 0-29.644-0.561-44.264-1.6l5.157-77.719c12.919 0.928 25.958 1.424 39.106 1.424z" />
<glyph unicode="&#xe902;" glyph-name="tailscale" d="M131.2 323.8c70.6 0 127.8 57.2 127.8 127.8s-57.2 127.8-127.8 127.8-127.6-57.4-127.6-127.8 57.2-127.8 127.6-127.8zM514.4 323.8c70.6 0 127.8 57.2 127.8 127.8s-57.2 127.8-127.8 127.8c-70.6 0-127.8-57.2-127.8-127.8s57.2-127.8 127.8-127.8zM514.4-64c70.6 0 127.8 57.2 127.8 127.8s-57.2 127.8-127.8 127.8c-70.6 0-127.8-57.2-127.8-127.8s57.2-127.8 127.8-127.8zM892.8 323.8c70.6 0 127.8 57.2 127.8 127.8s-57.2 127.8-127.8 127.8c-70.6 0-127.8-57.2-127.8-127.8s57.2-127.8 127.8-127.8z" />
<glyph unicode="&#xe903;" glyph-name="torproxy" d="M750.016 439.488c-32.512 29.504-73.504 53.344-115.328 77.152-19.008 10.496-77.344 56.16-57.152 120.992l-36.32 15.328c57.152 88.672 131.68 176.32 223.008 258.336-73.344-24.672-138.176-62.848-186.848-130.496 28.672 60 75.328 119.168 126.848 179.168-70.496-50.496-131.488-107.68-169.664-184l26.656 106.848c-38.176-68.672-64.832-138.336-75.328-207.84l-56.16 22.848-9.504-7.68c49.504-88.672 23.84-135.328-0.992-151.68-49.504-33.344-120.992-76.16-157.344-113.344-68.672-70.656-88.672-137.344-82.016-226.016 6.656-113.504 89.664-207.84 199.328-244.992 48.672-16.32 93.344-18.176 143.008-18.176 80 0 162.016 20.992 222.176 71.488 63.84 52.992 100.832 131.488 100.992 214.496 0.32 82.656-34.336 161.664-95.328 217.504zM598.336 60.832c-3.84-17.152-16.16-38.176-31.328-57.152 5.664 10.496 10.496 20.992 13.344 32.512 23.84 84.832 34.336 123.84 22.848 217.344-1.824 9.504-5.664 40-20 73.344-20 50.656-50.496 98.336-54.336 108.832-6.656 16.16-16.16 84.832-17.152 131.488 0.992-40 3.84-113.344 14.336-142.016 2.848-9.664 30.496-52.512 50.496-104.832 13.344-36.32 16.16-69.664 19.008-79.168 9.664-43.008-1.824-115.488-16.992-184-4.832-24.832-18.176-53.504-35.328-75.328 9.504 13.344 17.152 30.496 22.848 50.496 11.488 40 16.16 91.488 15.168 124-0.832 19.008-9.504 60-23.84 97.152-8.512 20-20.992 40.992-29.504 55.328-9.504 14.336-9.504 45.664-13.344 82.016 0.832-39.168-2.848-59.168 6.656-86.848 5.664-16.16 26.656-39.008 32.32-60.992 8.672-29.504 17.152-62.016 16.32-82.016 0-22.848-0.992-64.832-11.488-110.656-6.656-34.176-22.016-63.84-46.656-82.848 10.496 13.344 16.16 26.656 19.008 40 3.84 20 4.832 39.168 6.656 63.008 2.016 24.512 0.32 49.344-4.672 73.344-7.68 34.336-20 68.672-25.824 92.512 0.992-26.656 11.488-60 16.32-95.328 3.68-25.824 1.824-51.488 0.832-74.336-0.832-26.656-9.504-73.504-20.992-96.32-11.488 4.832-15.168 11.488-22.848 20.992-9.664 12.32-15.328 25.664-20.992 40.992-5.344 12.672-9.504 25.664-12.512 39.008-4.512 33.504 4.16 67.168 23.84 94.496 20 28.672 24 30.496 30.496 63.84-9.504-29.504-16.16-32.32-37.152-57.152-23.84-27.68-27.488-67.68-27.488-100.16 0-13.344 5.664-28.672 10.496-43.008 5.664-15.168 11.328-30.336 19.008-41.824 5.664-9.504 13.344-16.16 20-20.992-24.832 6.656-50.496 16.16-66.656 29.504-40 34.496-75.328 92.512-80.16 144-3.84 42.016 34.336 103.008 88.672 133.504 45.824 26.656 56.32 56.32 65.824 104.992-13.344-42.016-26.656-78.336-70.656-100.16-62.848-34.336-95.328-89.664-92.32-143.008 4.672-67.68 31.328-114.496 85.824-151.68 12.32-8.672 29.504-17.152 47.68-23.84-67.84 16.16-76.32 25.664-99.168 52.32 0 2.016-5.824 5.824-5.824 6.656-30.496 34.336-68.512 93.504-82.016 147.84-4.672 19.008-9.504 39.008-3.68 58.176 24.672 89.664 79.008 124 133.344 160.992 13.504 9.664 26.848 18.176 39.168 27.68 30.496 24 38.176 85.824 44.832 121.152-12.32-43.008-25.824-96.32-49.664-113.504-12.32-9.504-27.68-17.152-40-25.664-56.16-38.176-112.512-74.496-138.176-166.848-5.824-24-2.016-41.152 3.68-64 14.336-56.16 52.512-117.152 84.992-153.504l5.664-5.664c14.336-16.32 32.512-28.672 54.336-37.152-19.168 4.512-37.664 11.168-55.328 20-88.672 42.848-147.68 135.328-151.488 210.656-7.68 153.504 65.824 198.336 134.336 254.656 38.176 31.328 91.68 46.656 122.176 102.848 5.664 12.512 9.504 39.168 1.824 67.84-2.848 9.504-17.152 43.84-22.848 51.488l84.832-37.344c-1.824-40-2.848-72.32 4.672-102.016 8.672-32.32 50.656-79.008 67.84-133.504 33.344-102.848 24.832-237.152 0.832-342.176z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -1,23 +1,20 @@
/*!
* RaspAP-Brands Brand Icons - https://raspap.com
* License - https://github.com/billz/RaspAP-Brands-webgui/blob/master/LICENSE
*/
@font-face {
font-family: 'RaspAP';
src: url('fonts/RaspAP.eot?e76qs3');
src: url('fonts/RaspAP.eot?e76qs3#iefix') format('embedded-opentype'),
url('fonts/RaspAP.ttf?e76qs3') format('truetype'),
url('fonts/RaspAP.woff?e76qs3') format('woff'),
url('fonts/RaspAP.svg?e76qs3#RaspAP') format('svg');
src: url('fonts/RaspAP.eot?3vjloy');
src: url('fonts/RaspAP.eot?3vjloy#iefix') format('embedded-opentype'),
url('fonts/RaspAP.ttf?3vjloy') format('truetype'),
url('fonts/RaspAP.woff?3vjloy') format('woff'),
url('fonts/RaspAP.svg?3vjloy#RaspAP') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}
[class^="ra-"], [class*=" ra-"] {
/* use !important to prevent issues with browser extensions that change ..webfonts */
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'RaspAP' !important;
speak: none;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
@@ -30,24 +27,38 @@
}
.ra-wireguard:before {
font-size: 1.2rem;
font-size: 1.1rem;
content: "\e900";
vertical-align: middle;
}
.card-header .ra-wireguard:before {
color: #fff;
}
.sidebar .nav-item.active .nav-link
span.ra-wireguard:before {
color: #6e707e;
}
.ra-raspap:before {
font-size: 4.35rem;
content: "\e901";
color: #2b8080;
margin-left: 0.1em;
}
.ra-tailscale:before {
font-size: 1.1rem;
content: "\e902";
vertical-align: top;
}
.ra-torproxy:before {
font-size: 1.3rem;
content: "\e903";
vertical-align: top;
}
.card-header .ra-wireguard:before,
.card-header .ra-tailscale:before,
.card-header .ra-torproxy:before {
color: #fff;
}
.sidebar .nav-item.active .nav-link span.ra-wireguard:before,
.sidebar .nav-item.active .nav-link span.ra-tailscale:before,
.sidebar .nav-item.active .nav-link span.ra-torproxy:before {
color: #6e707e;
}
.sb-nav-link-icon .ra-tailscale {
margin-bottom: 0.4rem;
}

View File

@@ -13,9 +13,9 @@ window.addEventListener('DOMContentLoaded', event => {
const sidebarToggle = document.body.querySelector('#sidebarToggle');
if (sidebarToggle) {
// Uncomment below to persist sidebar toggle between refreshes
if (localStorage.getItem('sb|sidebar-toggle') === 'true') {
document.body.classList.toggle('sb-sidenav-toggled');
}
// if (localStorage.getItem('sb|sidebar-toggle') === 'true') {
// document.body.classList.toggle('sb-sidenav-toggled');
// }
sidebarToggle.addEventListener('click', event => {
event.preventDefault();
document.body.classList.toggle('sb-sidenav-toggled');

14
dist/speedtest/backend/empty.php vendored Executable file
View File

@@ -0,0 +1,14 @@
<?php
header('HTTP/1.1 200 OK');
if (isset($_GET['cors'])) {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Encoding, Content-Type');
}
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Connection: keep-alive');

66
dist/speedtest/backend/garbage.php vendored Executable file
View File

@@ -0,0 +1,66 @@
<?php
// Disable Compression
@ini_set('zlib.output_compression', 'Off');
@ini_set('output_buffering', 'Off');
@ini_set('output_handler', '');
/**
* @return int
*/
function getChunkCount()
{
if (!array_key_exists('ckSize', $_GET)
|| !ctype_digit($_GET['ckSize'])
|| (int) $_GET['ckSize'] <= 0
) {
return 4;
}
if ((int) $_GET['ckSize'] > 1024) {
return 1024;
}
return (int) $_GET['ckSize'];
}
/**
* @return void
*/
function sendHeaders()
{
header('HTTP/1.1 200 OK');
if (isset($_GET['cors'])) {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
}
// Indicate a file download
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=random.dat');
header('Content-Transfer-Encoding: binary');
// Cache settings: never cache this request
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
}
// Determine how much data we should send
$chunks = getChunkCount();
// Generate data
if (function_exists('random_bytes')) {
$data = random_bytes(1048576);
} else {
$data = openssl_random_pseudo_bytes(1048576);
}
// Deliver chunks of 1048576 bytes
sendHeaders();
for ($i = 0; $i < $chunks; $i++) {
echo $data;
flush();
}

325
dist/speedtest/backend/getIP.php vendored Executable file
View File

@@ -0,0 +1,325 @@
<?php
/*
* This script detects the client's IP address and fetches ISP info from ipinfo.io/
* Output from this script is a JSON string composed of 2 objects: a string called processedString which contains the combined IP, ISP, Country and distance as it can be presented to the user; and an object called rawIspInfo which contains the raw data from ipinfo.io (will be empty if isp detection is disabled).
* Client side, the output of this script can be treated as JSON or as regular text. If the output is regular text, it will be shown to the user as is.
*/
error_reporting(0);
define('API_KEY_FILE', 'getIP_ipInfo_apikey.php');
define('SERVER_LOCATION_CACHE_FILE', 'getIP_serverLocation.php');
require_once 'getIP_util.php';
/**
* @param string $ip
*
* @return string|null
*/
function getLocalOrPrivateIpInfo($ip)
{
// ::1/128 is the only localhost ipv6 address. there are no others, no need to strpos this
if ('::1' === $ip) {
return 'localhost IPv6 access';
}
// simplified IPv6 link-local address (should match fe80::/10)
if (stripos($ip, 'fe80:') === 0) {
return 'link-local IPv6 access';
}
// anything within the 127/8 range is localhost ipv4, the ip must start with 127.0
if (strpos($ip, '127.') === 0) {
return 'localhost IPv4 access';
}
// 10/8 private IPv4
if (strpos($ip, '10.') === 0) {
return 'private IPv4 access';
}
// 172.16/12 private IPv4
if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) {
return 'private IPv4 access';
}
// 192.168/16 private IPv4
if (strpos($ip, '192.168.') === 0) {
return 'private IPv4 access';
}
// IPv4 link-local
if (strpos($ip, '169.254.') === 0) {
return 'link-local IPv4 access';
}
return null;
}
/**
* @return string
*/
function getIpInfoTokenString()
{
if (!file_exists(API_KEY_FILE)
|| !is_readable(API_KEY_FILE)
) {
return '';
}
include API_KEY_FILE;
if (empty($IPINFO_APIKEY)) {
return '';
}
return '?token='.$IPINFO_APIKEY;
}
/**
* @param string $ip
*
* @return array|null
*/
function getIspInfo($ip)
{
$json = file_get_contents('https://ipinfo.io/'.$ip.'/json'.getIpInfoTokenString());
if (!is_string($json)) {
return null;
}
$data = json_decode($json, true);
if (!is_array($data)) {
return null;
}
return $data;
}
/**
* @param array|null $rawIspInfo
*
* @return string
*/
function getIsp($rawIspInfo)
{
if (!is_array($rawIspInfo)
|| !array_key_exists('org', $rawIspInfo)
|| !is_string($rawIspInfo['org'])
|| empty($rawIspInfo['org'])
) {
return 'Unknown ISP';
}
// Remove AS##### from ISP name, if present
return preg_replace('/AS\\d+\\s/', '', $rawIspInfo['org']);
}
/**
* @return string|null
*/
function getServerLocation()
{
$serverLoc = null;
if (file_exists(SERVER_LOCATION_CACHE_FILE)
&& is_readable(SERVER_LOCATION_CACHE_FILE)
) {
include SERVER_LOCATION_CACHE_FILE;
}
if (is_string($serverLoc) && !empty($serverLoc)) {
return $serverLoc;
}
$json = file_get_contents('https://ipinfo.io/json'.getIpInfoTokenString());
if (!is_string($json)) {
return null;
}
$details = json_decode($json, true);
if (!is_array($details)
|| !array_key_exists('loc', $details)
|| !is_string($details['loc'])
|| empty($details['loc'])
) {
return null;
}
$serverLoc = $details['loc'];
$cacheData = "<?php\n\n\$serverLoc = '".addslashes($serverLoc)."';\n";
file_put_contents(SERVER_LOCATION_CACHE_FILE, $cacheData);
return $serverLoc;
}
/**
* Optimized algorithm from http://www.codexworld.com
*
* @param float $latitudeFrom
* @param float $longitudeFrom
* @param float $latitudeTo
* @param float $longitudeTo
*
* @return float [km]
*/
function distance(
$latitudeFrom,
$longitudeFrom,
$latitudeTo,
$longitudeTo
) {
$rad = M_PI / 180;
$theta = $longitudeFrom - $longitudeTo;
$dist = sin($latitudeFrom * $rad)
* sin($latitudeTo * $rad)
+ cos($latitudeFrom * $rad)
* cos($latitudeTo * $rad)
* cos($theta * $rad);
return acos($dist) / $rad * 60 * 1.853;
}
/**
* @param array|null $rawIspInfo
*
* @return string|null
*/
function getDistance($rawIspInfo)
{
if (!is_array($rawIspInfo)
|| !array_key_exists('loc', $rawIspInfo)
|| !isset($_GET['distance'])
|| !in_array($_GET['distance'], ['mi', 'km'], true)
) {
return null;
}
$unit = $_GET['distance'];
$clientLocation = $rawIspInfo['loc'];
$serverLocation = getServerLocation();
if (!is_string($serverLocation)) {
return null;
}
return calculateDistance(
$serverLocation,
$clientLocation,
$unit
);
}
/**
* @param string $clientLocation
* @param string $serverLocation
* @param string $unit
*
* @return string
*/
function calculateDistance($clientLocation, $serverLocation, $unit)
{
list($clientLatitude, $clientLongitude) = explode(',', $clientLocation);
list($serverLatitude, $serverLongitude) = explode(',', $serverLocation);
$dist = distance(
$clientLatitude,
$clientLongitude,
$serverLatitude,
$serverLongitude
);
if ('mi' === $unit) {
$dist /= 1.609344;
$dist = round($dist, -1);
if ($dist < 15) {
$dist = '<15';
}
return $dist.' mi';
}
if ('km' === $unit) {
$dist = round($dist, -1);
if ($dist < 20) {
$dist = '<20';
}
return $dist.' km';
}
return null;
}
/**
* @return void
*/
function sendHeaders()
{
header('Content-Type: application/json; charset=utf-8');
if (isset($_GET['cors'])) {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
}
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
}
/**
* @param string $ip
* @param string|null $ipInfo
* @param string|null $distance
* @param array|null $rawIspInfo
*
* @return void
*/
function sendResponse(
$ip,
$ipInfo = null,
$distance = null,
$rawIspInfo = null
) {
$processedString = $ip;
if (is_string($ipInfo)) {
$processedString .= ' - '.$ipInfo;
}
if (is_array($rawIspInfo)
&& array_key_exists('country', $rawIspInfo)
) {
$processedString .= ', '.$rawIspInfo['country'];
}
if (is_string($distance)) {
$processedString .= ' ('.$distance.')';
}
sendHeaders();
echo json_encode(
[
'processedString' => $processedString,
'rawIspInfo' => $rawIspInfo ?: '',
]
);
}
$ip = getClientIp();
$localIpInfo = getLocalOrPrivateIpInfo($ip);
// local ip, no need to fetch further information
if (is_string($localIpInfo)) {
sendResponse($ip, $localIpInfo);
exit;
}
if (!isset($_GET['isp'])) {
sendResponse($ip);
exit;
}
$rawIspInfo = getIspInfo($ip);
$isp = getIsp($rawIspInfo);
$distance = getDistance($rawIspInfo);
sendResponse($ip, $isp, $distance, $rawIspInfo);

View File

@@ -0,0 +1,4 @@
<?php
// put your token between the quotes if you have one
$IPINFO_APIKEY = '';

View File

@@ -0,0 +1,3 @@
<?php
$serverLoc = '45.9237,6.8693';

21
dist/speedtest/backend/getIP_util.php vendored Executable file
View File

@@ -0,0 +1,21 @@
<?php
/**
* @return string
*/
function getClientIp()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$ip = $_SERVER['HTTP_X_REAL_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$ip = preg_replace('/,.*/', '', $ip); // hosts are comma-separated, client is first
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return preg_replace('/^::ffff:/', '', $ip);
}

231
dist/speedtest/speedtest.css vendored Normal file
View File

@@ -0,0 +1,231 @@
#testWrapper {
text-align: center;
}
#loading {
background-color: #FFFFFF;
color: #404040;
text-align: center;
}
span.loadCircle {
display: inline-block;
width: 2em;
height: 2em;
vertical-align: middle;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAP1BMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZyFzwnAAAAFHRSTlMAEvRFvX406baecwbf0casimhSHyiwmqgAAADpSURBVHja7dbJbQMxAENRahnN5lkc//5rDRAkDeRgHszXgACJoKiIiIiIiIiIiIiIiIiIiIj4HHspsrpAVhdVVguzrA4OWc10WcEqpwKbnBo0OU1Q5NSpsoJFTgOecrrdEag85DRgktNqfoEdTjnd7hrEHMEJvmRUYJbTYk5Agy6nau6Abp5Cm7mDBtRdPi9gyKdU7w4p1fsLvyqs8hl4z9/w3n/Hmr9WoQ65lAU4d7lMYOz//QboRR5jBZibLMZdAR6O/Vfa1PlxNr3XdS3HzK/HVPRu/KnLs8iAOh993VpRRERERMT/fAN60wwWaVyWwAAAAABJRU5ErkJggg==');
background-size: 2em 2em;
margin-right: 0.5em;
animation: spin 0.6s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
}
#startStopBtn {
display: inline-block;
margin: 0 auto;
transition: all 0.3s;
box-sizing: border-box;
width: 8em;
line-height: 2.5em;
cursor: pointer;
}
#startStopBtn.running {
background-color: #FF3030;
border-color: #FF3030;
color: #FFFFFF;
line-height: 2.5em;
}
#startStopBtn:before {
content: "Start";
}
#startStopBtn.running:before {
content: "Abort";
}
#serverArea {
margin-top: 1em;
}
#server {
font-size: 1em;
padding: 0.2em;
}
#test {
margin-top: 2em;
margin-bottom: 1em;
}
div.testArea {
display: inline-block;
width: 16em;
height: 12.5em;
position: relative;
box-sizing: border-box;
}
div.testArea2 {
display: inline-block;
width: 14em;
height: 7em;
position: relative;
box-sizing: border-box;
text-align: center;
}
div.testArea div.testName {
position: absolute;
top: -0.3em;
left: 0;
width: 100%;
font-size: 1.4em;
z-index: 9;
}
div.testArea2 div.testName {
display: block;
text-align: center;
font-size: 1.4em;
}
div.testArea div.meterText {
position: absolute;
bottom: 1.55em;
left: 0;
width: 100%;
font-size: 2.5em;
z-index: 9;
}
div.testArea2 div.meterText {
display: inline-block;
font-size: 2.5em;
}
div.meterText:empty:before {
content: "0.00";
}
div.testArea div.unit {
position: absolute;
bottom: 2em;
left: 0;
width: 100%;
z-index: 9;
}
div.testArea2 div.unit {
display: inline-block;
}
div.testArea canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
div.testGroup {
display: block;
margin: 0 auto;
}
#shareArea {
width: 95%;
max-width: 40em;
margin: 0 auto;
margin-top: 2em;
}
#shareArea>* {
display: block;
width: 100%;
height: auto;
margin: 0.25em 0;
}
#privacyPolicy {
position: fixed;
top: 2em;
bottom: 2em;
left: 2em;
right: 2em;
overflow-y: auto;
margin: 0 auto;
width: 50%;
height: auto;
box-shadow: 0 0 3em 1em #333;
z-index: 999999;
text-align: left;
background-color: #FFFFFF;
padding: 1em;
border-radius: 0.3em;
color: #858796;
}
#privacyPolicy h4, h5 {
color: #212529;
}
a.privacy {
text-align: center;
font-size: 0.8em;
color: #808080;
display: block;
}
@media all and (max-width:40em) {
body {
font-size: 0.8em;
}
}
div.visible {
animation: fadeIn 0.4s;
display: block;
}
div.hidden {
animation: fadeOut 0.4s;
display: none;
}
div.centered {
margin: 0 auto;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
display: block;
opacity: 1;
}
100% {
display: block;
opacity: 0;
}
}

327
dist/speedtest/speedtest.js vendored Executable file
View File

@@ -0,0 +1,327 @@
/*
LibreSpeed - Main
by Federico Dossena
https://github.com/librespeed/speedtest/
GNU LGPLv3 License
*/
function Speedtest() {
this._serverList = []; //when using multiple points of test, this is a list of test points
this._selectedServer = null; //when using multiple points of test, this is the selected server
this._settings = {}; //settings for the speedtest worker
this._state = 0; //0=adding settings, 1=adding servers, 2=server selection done, 3=test running, 4=done
}
Speedtest.prototype = {
constructor: Speedtest,
/**
* Returns the state of the test: 0=adding settings, 1=adding servers, 2=server selection done, 3=test running, 4=done
*/
getState: function() {
return this._state;
},
/**
* Change one of the test settings from their defaults.
* - parameter: string with the name of the parameter that you want to set
* - value: new value for the parameter
*
* Invalid values or nonexistant parameters will be ignored by the speedtest worker.
*/
setParameter: function(parameter, value) {
if (this._state == 3)
throw "You cannot change the test settings while running the test";
this._settings[parameter] = value;
if(parameter === "telemetry_extra"){
this._originalExtra=this._settings.telemetry_extra;
}
},
/**
* Used internally to check if a server object contains all the required elements.
* Also fixes the server URL if needed.
*/
_checkServerDefinition: function(server) {
try {
if (typeof server.name !== "string")
throw "Name string missing from server definition (name)";
if (typeof server.server !== "string")
throw "Server address string missing from server definition (server)";
if (server.server.charAt(server.server.length - 1) != "/")
server.server += "/";
if (server.server.indexOf("//") == 0)
server.server = location.protocol + server.server;
if (typeof server.dlURL !== "string")
throw "Download URL string missing from server definition (dlURL)";
if (typeof server.ulURL !== "string")
throw "Upload URL string missing from server definition (ulURL)";
if (typeof server.pingURL !== "string")
throw "Ping URL string missing from server definition (pingURL)";
if (typeof server.getIpURL !== "string")
throw "GetIP URL string missing from server definition (getIpURL)";
} catch (e) {
throw "Invalid server definition";
}
},
/**
* Add a test point (multiple points of test)
* server: the server to be added as an object. Defined in app/js/speedtestUI.js
*/
addTestPoint: function(server) {
this._checkServerDefinition(server);
if (this._state == 0) this._state = 1;
if (this._state != 1) throw "You can't add a server after server selection";
this._settings.mpot = true;
this._serverList.push(server);
},
/**
* Same as addTestPoint, but you can pass an array of servers
*/
addTestPoints: function(list) {
for (var i = 0; i < list.length; i++) this.addTestPoint(list[i]);
},
/**
* Load a JSON server list from URL (multiple points of test)
* url: the url where the server list can be fetched. Must be an array with objects containing the following elements:
* result: callback to be called when the list is loaded correctly. An array with the loaded servers will be passed to this function, or null if it failed
*/
loadServerList: function(url,result) {
if (this._state == 0) this._state = 1;
if (this._state != 1) throw "You can't add a server after server selection";
this._settings.mpot = true;
var xhr = new XMLHttpRequest();
xhr.onload = function(){
try{
var servers=JSON.parse(xhr.responseText);
for(var i=0;i<servers.length;i++){
this._checkServerDefinition(servers[i]);
}
this.addTestPoints(servers);
result(servers);
}catch(e){
result(null);
}
}.bind(this);
xhr.onerror = function(){result(null);}
xhr.open("GET",url);
xhr.send();
},
/**
* Returns the selected server (multiple points of test)
*/
getSelectedServer: function() {
if (this._state < 2 || this._selectedServer == null)
throw "No server is selected";
return this._selectedServer;
},
/**
* Manually selects one of the test points (multiple points of test)
*/
setSelectedServer: function(server) {
this._checkServerDefinition(server);
if (this._state == 3)
throw "You can't select a server while the test is running";
this._selectedServer = server;
this._state = 2;
},
/**
* Automatically selects a server from the list of added test points. The server with the lowest ping will be chosen. (multiple points of test)
* The process is asynchronous and the passed result callback function will be called when it's done, then the test can be started.
*/
selectServer: function(result) {
if (this._state != 1) {
if (this._state == 0) throw "No test points added";
if (this._state == 2) throw "Server already selected";
if (this._state >= 3)
throw "You can't select a server while the test is running";
}
if (this._selectServerCalled) throw "selectServer already called"; else this._selectServerCalled=true;
/*this function goes through a list of servers. For each server, the ping is measured, then the server with the function selected is called with the best server, or null if all the servers were down.
*/
var select = function(serverList, selected) {
//pings the specified URL, then calls the function result. Result will receive a parameter which is either the time it took to ping the URL, or -1 if something went wrong.
var PING_TIMEOUT = 2000;
var USE_PING_TIMEOUT = true; //will be disabled on unsupported browsers
if (/MSIE.(\d+\.\d+)/i.test(navigator.userAgent)) {
//IE11 doesn't support XHR timeout
USE_PING_TIMEOUT = false;
}
var ping = function(url, rtt) {
url += (url.match(/\?/) ? "&" : "?") + "cors=true";
var xhr = new XMLHttpRequest();
var t = new Date().getTime();
xhr.onload = function() {
if (xhr.responseText.length == 0) {
//we expect an empty response
var instspd = new Date().getTime() - t; //rough timing estimate
try {
//try to get more accurate timing using performance API
var p = performance.getEntriesByName(url);
p = p[p.length - 1];
var d = p.responseStart - p.requestStart;
if (d <= 0) d = p.duration;
if (d > 0 && d < instspd) instspd = d;
} catch (e) {}
rtt(instspd);
} else rtt(-1);
}.bind(this);
xhr.onerror = function() {
rtt(-1);
}.bind(this);
xhr.open("GET", url);
if (USE_PING_TIMEOUT) {
try {
xhr.timeout = PING_TIMEOUT;
xhr.ontimeout = xhr.onerror;
} catch (e) {}
}
xhr.send();
}.bind(this);
/**
* This function repeatedly pings a server to get a good estimate of the ping. When it's done, it calls the done function without parameters.
* At the end of the execution, the server will have a new parameter called pingT, which is either the best ping we got from the server or -1 if something went wrong.
*/
var PINGS = 3, //up to 3 pings are performed, unless the server is down...
SLOW_THRESHOLD = 500; //...or one of the pings is above this threshold
var checkServer = function(server, done) {
var i = 0;
server.pingT = -1;
if (server.server.indexOf(location.protocol) == -1) done();
else {
var nextPing = function() {
if (i++ == PINGS) {
done();
return;
}
ping(
server.server + server.pingURL,
function(t) {
if (t >= 0) {
if (t < server.pingT || server.pingT == -1) server.pingT = t;
if (t < SLOW_THRESHOLD) nextPing();
else done();
} else done();
}.bind(this)
);
}.bind(this);
nextPing();
}
}.bind(this);
//check servers in list, one by one
var i = 0;
var done = function() {
var bestServer = null;
for (var i = 0; i < serverList.length; i++) {
if (
serverList[i].pingT != -1 &&
(bestServer == null || serverList[i].pingT < bestServer.pingT)
)
bestServer = serverList[i];
}
selected(bestServer);
}.bind(this);
var nextServer = function() {
if (i == serverList.length) {
done();
return;
}
checkServer(serverList[i++], nextServer);
}.bind(this);
nextServer();
}.bind(this);
//parallel server selection
var CONCURRENCY = 6;
var serverLists = [];
for (var i = 0; i < CONCURRENCY; i++) {
serverLists[i] = [];
}
for (var i = 0; i < this._serverList.length; i++) {
serverLists[i % CONCURRENCY].push(this._serverList[i]);
}
var completed = 0;
var bestServer = null;
for (var i = 0; i < CONCURRENCY; i++) {
select(
serverLists[i],
function(server) {
if (server != null) {
if (bestServer == null || server.pingT < bestServer.pingT)
bestServer = server;
}
completed++;
if (completed == CONCURRENCY) {
this._selectedServer = bestServer;
this._state = 2;
if (result) result(bestServer);
}
}.bind(this)
);
}
},
/**
* Starts the test.
* During the test, the onupdate(data) callback function will be called periodically with data from the worker.
* At the end of the test, the onend(aborted) function will be called with a boolean telling you if the test was aborted or if it ended normally.
*/
start: function() {
if (this._state == 3) throw "Test already running";
this.worker = new Worker("dist/speedtest/speedtest_worker.js?r=" + Math.random());
this.worker.onmessage = function(e) {
if (e.data === this._prevData) return;
else this._prevData = e.data;
var data = JSON.parse(e.data);
try {
if (this.onupdate) this.onupdate(data);
} catch (e) {
console.error("Speedtest onupdate event threw exception: " + e);
}
if (data.testState >= 4) {
clearInterval(this.updater);
this._state = 4;
try {
if (this.onend) this.onend(data.testState == 5);
} catch (e) {
console.error("Speedtest onend event threw exception: " + e);
}
}
}.bind(this);
this.updater = setInterval(
function() {
this.worker.postMessage("status");
}.bind(this),
200
);
if (this._state == 1)
throw "When using multiple points of test, you must call selectServer before starting the test";
if (this._state == 2) {
this._settings.url_dl =
this._selectedServer.server + this._selectedServer.dlURL;
this._settings.url_ul =
this._selectedServer.server + this._selectedServer.ulURL;
this._settings.url_ping =
this._selectedServer.server + this._selectedServer.pingURL;
this._settings.url_getIp =
this._selectedServer.server + this._selectedServer.getIpURL;
if (typeof this._originalExtra !== "undefined") {
this._settings.telemetry_extra = JSON.stringify({
server: this._selectedServer.name,
extra: this._originalExtra
});
} else
this._settings.telemetry_extra = JSON.stringify({
server: this._selectedServer.name
});
}
this._state = 3;
this.worker.postMessage("start " + JSON.stringify(this._settings));
},
/**
* Aborts the test while it's running.
*/
abort: function() {
if (this._state < 3) throw "You cannot abort a test that's not started yet";
if (this._state < 4) this.worker.postMessage("abort");
}
};

725
dist/speedtest/speedtest_worker.js vendored Executable file
View File

@@ -0,0 +1,725 @@
/*
LibreSpeed - Worker
by Federico Dossena
https://github.com/librespeed/speedtest/
GNU LGPLv3 License
*/
// data reported to main thread
var testState = -1; // -1=not started, 0=starting, 1=download test, 2=ping+jitter test, 3=upload test, 4=finished, 5=abort
var dlStatus = ""; // download speed in megabit/s with 2 decimal digits
var ulStatus = ""; // upload speed in megabit/s with 2 decimal digits
var pingStatus = ""; // ping in milliseconds with 2 decimal digits
var jitterStatus = ""; // jitter in milliseconds with 2 decimal digits
var clientIp = ""; // client's IP address as reported by getIP.php
var dlProgress = 0; //progress of download test 0-1
var ulProgress = 0; //progress of upload test 0-1
var pingProgress = 0; //progress of ping+jitter test 0-1
var testId = null; //test ID (sent back by telemetry if used, null otherwise)
var log = ""; //telemetry log
function tlog(s) {
if (settings.telemetry_level >= 2) {
log += Date.now() + ": " + s + "\n";
}
}
function tverb(s) {
if (settings.telemetry_level >= 3) {
log += Date.now() + ": " + s + "\n";
}
}
function twarn(s) {
if (settings.telemetry_level >= 2) {
log += Date.now() + " WARN: " + s + "\n";
}
console.warn(s);
}
// test settings. can be overridden by sending specific values with the start command
var settings = {
mpot: true, //set to true when in MPOT mode
test_order: "IP_D_U", //order in which tests will be performed as a string. D=Download, U=Upload, P=Ping+Jitter, I=IP, _=1 second delay
time_ul_max: 15, // max duration of upload test in seconds
time_dl_max: 15, // max duration of download test in seconds
time_auto: true, // if set to true, tests will take less time on faster connections
time_ulGraceTime: 3, //time to wait in seconds before actually measuring ul speed (wait for buffers to fill)
time_dlGraceTime: 1.5, //time to wait in seconds before actually measuring dl speed (wait for TCP window to increase)
count_ping: 10, // number of pings to perform in ping test
url_dl: "backend/garbage.php", // path to a large file or garbage.php, used for download test. must be relative to this js file
url_ul: "backend/empty.php", // path to an empty file, used for upload test. must be relative to this js file
url_ping: "backend/empty.php", // path to an empty file, used for ping test. must be relative to this js file
url_getIp: "backend/getIP.php", // path to getIP.php relative to this js file, or a similar thing that outputs the client's ip
getIp_ispInfo: true, //if set to true, the server will include ISP info with the IP address
getIp_ispInfo_distance: "km", //km or mi=estimate distance from server in km/mi; set to false to disable distance estimation. getIp_ispInfo must be enabled in order for this to work
xhr_dlMultistream: 6, // number of download streams to use (can be different if enable_quirks is active)
xhr_ulMultistream: 3, // number of upload streams to use (can be different if enable_quirks is active)
xhr_multistreamDelay: 300, //how much concurrent requests should be delayed
xhr_ignoreErrors: 1, // 0=fail on errors, 1=attempt to restart a stream if it fails, 2=ignore all errors
xhr_dlUseBlob: false, // if set to true, it reduces ram usage but uses the hard drive (useful with large garbagePhp_chunkSize and/or high xhr_dlMultistream)
xhr_ul_blob_megabytes: 20, //size in megabytes of the upload blobs sent in the upload test (forced to 4 on chrome mobile)
garbagePhp_chunkSize: 100, // size of chunks sent by garbage.php (can be different if enable_quirks is active)
enable_quirks: true, // enable quirks for specific browsers. currently it overrides settings to optimize for specific browsers, unless they are already being overridden with the start command
ping_allowPerformanceApi: true, // if enabled, the ping test will attempt to calculate the ping more precisely using the Performance API. Currently works perfectly in Chrome, badly in Edge, and not at all in Firefox. If Performance API is not supported or the result is obviously wrong, a fallback is provided.
overheadCompensationFactor: 1.06, //can be changed to compensatie for transport overhead. (see doc.md for some other values)
useMebibits: false, //if set to true, speed will be reported in mebibits/s instead of megabits/s
telemetry_level: 1, // 0=disabled, 1=basic (results only), 2=full (results and timing) 3=debug (results+log)
url_telemetry: "https://speedtest.raspap.com/results/telemetry.php", // path to the script that adds telemetry data to the database
telemetry_extra: "", //extra data that can be passed to the telemetry through the settings
forceIE11Workaround: false //when set to true, it will foce the IE11 upload test on all browsers. Debug only
};
var xhr = null; // array of currently active xhr requests
var interval = null; // timer used in tests
var test_pointer = 0; //pointer to the next test to run inside settings.test_order
/*
this function is used on URLs passed in the settings to determine whether we need a ? or an & as a separator
*/
function url_sep(url) {
return url.match(/\?/) ? "&" : "?";
}
/*
listener for commands from main thread to this worker.
commands:
-status: returns the current status as a JSON string containing testState, dlStatus, ulStatus, pingStatus, clientIp, jitterStatus, dlProgress, ulProgress, pingProgress
-abort: aborts the current test
-start: starts the test. optionally, settings can be passed as JSON.
example: start {"time_ul_max":"10", "time_dl_max":"10", "count_ping":"50"}
*/
this.addEventListener("message", function(e) {
var params = e.data.split(" ");
if (params[0] === "status") {
// return status
postMessage(
JSON.stringify({
testState: testState,
dlStatus: dlStatus,
ulStatus: ulStatus,
pingStatus: pingStatus,
clientIp: clientIp,
jitterStatus: jitterStatus,
dlProgress: dlProgress,
ulProgress: ulProgress,
pingProgress: pingProgress,
testId: testId
})
);
}
if (params[0] === "start" && testState === -1) {
// start new test
testState = 0;
try {
// parse settings, if present
var s = {};
try {
var ss = e.data.substring(5);
if (ss) s = JSON.parse(ss);
} catch (e) {
twarn("Error parsing custom settings JSON. Please check your syntax");
}
//copy custom settings
for (var key in s) {
if (typeof settings[key] !== "undefined") settings[key] = s[key];
else twarn("Unknown setting ignored: " + key);
}
var ua = navigator.userAgent;
// quirks for specific browsers. apply only if not overridden. more may be added in future releases
if (settings.enable_quirks || (typeof s.enable_quirks !== "undefined" && s.enable_quirks)) {
if (/Firefox.(\d+\.\d+)/i.test(ua)) {
if (typeof s.ping_allowPerformanceApi === "undefined") {
// ff performance API sucks
settings.ping_allowPerformanceApi = false;
}
}
if (/Edge.(\d+\.\d+)/i.test(ua)) {
if (typeof s.xhr_dlMultistream === "undefined") {
// edge more precise with 3 download streams
settings.xhr_dlMultistream = 3;
}
}
if (/Chrome.(\d+)/i.test(ua) && !!self.fetch) {
if (typeof s.xhr_dlMultistream === "undefined") {
// chrome more precise with 5 streams
settings.xhr_dlMultistream = 5;
}
}
}
if (/Edge.(\d+\.\d+)/i.test(ua)) {
//Edge 15 introduced a bug that causes onprogress events to not get fired, we have to use the "small chunks" workaround that reduces accuracy
settings.forceIE11Workaround = true;
}
if (/PlayStation 4.(\d+\.\d+)/i.test(ua)) {
//PS4 browser has the same bug as IE11/Edge
settings.forceIE11Workaround = true;
}
if (/Chrome.(\d+)/i.test(ua) && /Android|iPhone|iPad|iPod|Windows Phone/i.test(ua)) {
//cheap af
//Chrome mobile introduced a limitation somewhere around version 65, we have to limit XHR upload size to 4 megabytes
settings.xhr_ul_blob_megabytes = 4;
}
if (/^((?!chrome|android|crios|fxios).)*safari/i.test(ua)) {
//Safari also needs the IE11 workaround but only for the MPOT version
settings.forceIE11Workaround = true;
}
//telemetry_level has to be parsed and not just copied
if (typeof s.telemetry_level !== "undefined") settings.telemetry_level = s.telemetry_level === "basic" ? 1 : s.telemetry_level === "full" ? 2 : s.telemetry_level === "debug" ? 3 : 0; // telemetry level
//transform test_order to uppercase, just in case
settings.test_order = settings.test_order.toUpperCase();
} catch (e) {
twarn("Possible error in custom test settings. Some settings might not have been applied. Exception: " + e);
}
// run the tests
tverb(JSON.stringify(settings));
test_pointer = 0;
var iRun = false,
dRun = false,
uRun = false,
pRun = false;
var runNextTest = function() {
if (testState == 5) return;
if (test_pointer >= settings.test_order.length) {
//test is finished
if (settings.telemetry_level > 0)
sendTelemetry(function(id) {
testState = 4;
if (id != null) testId = id;
});
else testState = 4;
return;
}
switch (settings.test_order.charAt(test_pointer)) {
case "I":
{
test_pointer++;
if (iRun) {
runNextTest();
return;
} else iRun = true;
getIp(runNextTest);
}
break;
case "D":
{
test_pointer++;
if (dRun) {
runNextTest();
return;
} else dRun = true;
testState = 1;
dlTest(runNextTest);
}
break;
case "U":
{
test_pointer++;
if (uRun) {
runNextTest();
return;
} else uRun = true;
testState = 3;
ulTest(runNextTest);
}
break;
case "P":
{
test_pointer++;
if (pRun) {
runNextTest();
return;
} else pRun = true;
testState = 2;
pingTest(runNextTest);
}
break;
case "_":
{
test_pointer++;
setTimeout(runNextTest, 1000);
}
break;
default:
test_pointer++;
}
};
runNextTest();
}
if (params[0] === "abort") {
// abort command
if (testState >= 4) return;
tlog("manually aborted");
clearRequests(); // stop all xhr activity
runNextTest = null;
if (interval) clearInterval(interval); // clear timer if present
if (settings.telemetry_level > 1) sendTelemetry(function() {});
testState = 5; //set test as aborted
dlStatus = "";
ulStatus = "";
pingStatus = "";
jitterStatus = "";
clientIp = "";
dlProgress = 0;
ulProgress = 0;
pingProgress = 0;
}
});
// stops all XHR activity, aggressively
function clearRequests() {
tverb("stopping pending XHRs");
if (xhr) {
for (var i = 0; i < xhr.length; i++) {
try {
xhr[i].onprogress = null;
xhr[i].onload = null;
xhr[i].onerror = null;
} catch (e) {}
try {
xhr[i].upload.onprogress = null;
xhr[i].upload.onload = null;
xhr[i].upload.onerror = null;
} catch (e) {}
try {
xhr[i].abort();
} catch (e) {}
try {
delete xhr[i];
} catch (e) {}
}
xhr = null;
}
}
// gets client's IP using url_getIp, then calls the done function
var ipCalled = false; // used to prevent multiple accidental calls to getIp
var ispInfo = ""; //used for telemetry
function getIp(done) {
tverb("getIp");
if (ipCalled) return;
else ipCalled = true; // getIp already called?
var startT = new Date().getTime();
xhr = new XMLHttpRequest();
xhr.onload = function() {
tlog("IP: " + xhr.responseText + ", took " + (new Date().getTime() - startT) + "ms");
try {
var data = JSON.parse(xhr.responseText);
clientIp = data.processedString;
ispInfo = data.rawIspInfo;
} catch (e) {
clientIp = xhr.responseText;
ispInfo = "";
}
done();
};
xhr.onerror = function() {
tlog("getIp failed, took " + (new Date().getTime() - startT) + "ms");
done();
};
xhr.open("GET", settings.url_getIp + url_sep(settings.url_getIp) + (settings.mpot ? "cors=true&" : "") + (settings.getIp_ispInfo ? "isp=true" + (settings.getIp_ispInfo_distance ? "&distance=" + settings.getIp_ispInfo_distance + "&" : "&") : "&") + "r=" + Math.random(), true);
xhr.send();
}
// download test, calls done function when it's over
var dlCalled = false; // used to prevent multiple accidental calls to dlTest
function dlTest(done) {
tverb("dlTest");
if (dlCalled) return;
else dlCalled = true; // dlTest already called?
var totLoaded = 0.0, // total number of loaded bytes
startT = new Date().getTime(), // timestamp when test was started
bonusT = 0, //how many milliseconds the test has been shortened by (higher on faster connections)
graceTimeDone = false, //set to true after the grace time is past
failed = false; // set to true if a stream fails
xhr = [];
// function to create a download stream. streams are slightly delayed so that they will not end at the same time
var testStream = function(i, delay) {
setTimeout(
function() {
if (testState !== 1) return; // delayed stream ended up starting after the end of the download test
tverb("dl test stream started " + i + " " + delay);
var prevLoaded = 0; // number of bytes loaded last time onprogress was called
var x = new XMLHttpRequest();
xhr[i] = x;
xhr[i].onprogress = function(event) {
tverb("dl stream progress event " + i + " " + event.loaded);
if (testState !== 1) {
try {
x.abort();
} catch (e) {}
} // just in case this XHR is still running after the download test
// progress event, add number of new loaded bytes to totLoaded
var loadDiff = event.loaded <= 0 ? 0 : event.loaded - prevLoaded;
if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return; // just in case
totLoaded += loadDiff;
prevLoaded = event.loaded;
}.bind(this);
xhr[i].onload = function() {
// the large file has been loaded entirely, start again
tverb("dl stream finished " + i);
try {
xhr[i].abort();
} catch (e) {} // reset the stream data to empty ram
testStream(i, 0);
}.bind(this);
xhr[i].onerror = function() {
// error
tverb("dl stream failed " + i);
if (settings.xhr_ignoreErrors === 0) failed = true; //abort
try {
xhr[i].abort();
} catch (e) {}
delete xhr[i];
if (settings.xhr_ignoreErrors === 1) testStream(i, 0); //restart stream
}.bind(this);
// send xhr
try {
if (settings.xhr_dlUseBlob) xhr[i].responseType = "blob";
else xhr[i].responseType = "arraybuffer";
} catch (e) {}
xhr[i].open("GET", settings.url_dl + url_sep(settings.url_dl) + (settings.mpot ? "cors=true&" : "") + "r=" + Math.random() + "&ckSize=" + settings.garbagePhp_chunkSize, true); // random string to prevent caching
xhr[i].send();
}.bind(this),
1 + delay
);
}.bind(this);
// open streams
for (var i = 0; i < settings.xhr_dlMultistream; i++) {
testStream(i, settings.xhr_multistreamDelay * i);
}
// every 200ms, update dlStatus
interval = setInterval(
function() {
tverb("DL: " + dlStatus + (graceTimeDone ? "" : " (in grace time)"));
var t = new Date().getTime() - startT;
if (graceTimeDone) dlProgress = (t + bonusT) / (settings.time_dl_max * 1000);
if (t < 200) return;
if (!graceTimeDone) {
if (t > 1000 * settings.time_dlGraceTime) {
if (totLoaded > 0) {
// if the connection is so slow that we didn't get a single chunk yet, do not reset
startT = new Date().getTime();
bonusT = 0;
totLoaded = 0.0;
}
graceTimeDone = true;
}
} else {
var speed = totLoaded / (t / 1000.0);
if (settings.time_auto) {
//decide how much to shorten the test. Every 200ms, the test is shortened by the bonusT calculated here
var bonus = (5.0 * speed) / 100000;
bonusT += bonus > 400 ? 400 : bonus;
}
//update status
dlStatus = ((speed * 8 * settings.overheadCompensationFactor) / (settings.useMebibits ? 1048576 : 1000000)).toFixed(2); // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 or 1000000 to go to megabits/mebibits
if ((t + bonusT) / 1000.0 > settings.time_dl_max || failed) {
// test is over, stop streams and timer
if (failed || isNaN(dlStatus)) dlStatus = "Fail";
clearRequests();
clearInterval(interval);
dlProgress = 1;
tlog("dlTest: " + dlStatus + ", took " + (new Date().getTime() - startT) + "ms");
done();
}
}
}.bind(this),
200
);
}
// upload test, calls done function whent it's over
var ulCalled = false; // used to prevent multiple accidental calls to ulTest
function ulTest(done) {
tverb("ulTest");
if (ulCalled) return;
else ulCalled = true; // ulTest already called?
// garbage data for upload test
var r = new ArrayBuffer(1048576);
var maxInt = Math.pow(2, 32) - 1;
try {
r = new Uint32Array(r);
for (var i = 0; i < r.length; i++) r[i] = Math.random() * maxInt;
} catch (e) {}
var req = [];
var reqsmall = [];
for (var i = 0; i < settings.xhr_ul_blob_megabytes; i++) req.push(r);
req = new Blob(req);
r = new ArrayBuffer(262144);
try {
r = new Uint32Array(r);
for (var i = 0; i < r.length; i++) r[i] = Math.random() * maxInt;
} catch (e) {}
reqsmall.push(r);
reqsmall = new Blob(reqsmall);
var testFunction = function() {
var totLoaded = 0.0, // total number of transmitted bytes
startT = new Date().getTime(), // timestamp when test was started
bonusT = 0, //how many milliseconds the test has been shortened by (higher on faster connections)
graceTimeDone = false, //set to true after the grace time is past
failed = false; // set to true if a stream fails
xhr = [];
// function to create an upload stream. streams are slightly delayed so that they will not end at the same time
var testStream = function(i, delay) {
setTimeout(
function() {
if (testState !== 3) return; // delayed stream ended up starting after the end of the upload test
tverb("ul test stream started " + i + " " + delay);
var prevLoaded = 0; // number of bytes transmitted last time onprogress was called
var x = new XMLHttpRequest();
xhr[i] = x;
var ie11workaround;
if (settings.forceIE11Workaround) ie11workaround = true;
else {
try {
xhr[i].upload.onprogress;
ie11workaround = false;
} catch (e) {
ie11workaround = true;
}
}
if (ie11workaround) {
// IE11 workarond: xhr.upload does not work properly, therefore we send a bunch of small 256k requests and use the onload event as progress. This is not precise, especially on fast connections
xhr[i].onload = xhr[i].onerror = function() {
tverb("ul stream progress event (ie11wa)");
totLoaded += reqsmall.size;
testStream(i, 0);
};
xhr[i].open("POST", settings.url_ul + url_sep(settings.url_ul) + (settings.mpot ? "cors=true&" : "") + "r=" + Math.random(), true); // random string to prevent caching
try {
xhr[i].setRequestHeader("Content-Encoding", "identity"); // disable compression (some browsers may refuse it, but data is incompressible anyway)
} catch (e) {}
//No Content-Type header in MPOT branch because it triggers bugs in some browsers
xhr[i].send(reqsmall);
} else {
// REGULAR version, no workaround
xhr[i].upload.onprogress = function(event) {
tverb("ul stream progress event " + i + " " + event.loaded);
if (testState !== 3) {
try {
x.abort();
} catch (e) {}
} // just in case this XHR is still running after the upload test
// progress event, add number of new loaded bytes to totLoaded
var loadDiff = event.loaded <= 0 ? 0 : event.loaded - prevLoaded;
if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return; // just in case
totLoaded += loadDiff;
prevLoaded = event.loaded;
}.bind(this);
xhr[i].upload.onload = function() {
// this stream sent all the garbage data, start again
tverb("ul stream finished " + i);
testStream(i, 0);
}.bind(this);
xhr[i].upload.onerror = function() {
tverb("ul stream failed " + i);
if (settings.xhr_ignoreErrors === 0) failed = true; //abort
try {
xhr[i].abort();
} catch (e) {}
delete xhr[i];
if (settings.xhr_ignoreErrors === 1) testStream(i, 0); //restart stream
}.bind(this);
// send xhr
xhr[i].open("POST", settings.url_ul + url_sep(settings.url_ul) + (settings.mpot ? "cors=true&" : "") + "r=" + Math.random(), true); // random string to prevent caching
try {
xhr[i].setRequestHeader("Content-Encoding", "identity"); // disable compression (some browsers may refuse it, but data is incompressible anyway)
} catch (e) {}
//No Content-Type header in MPOT branch because it triggers bugs in some browsers
xhr[i].send(req);
}
}.bind(this),
delay
);
}.bind(this);
// open streams
for (var i = 0; i < settings.xhr_ulMultistream; i++) {
testStream(i, settings.xhr_multistreamDelay * i);
}
// every 200ms, update ulStatus
interval = setInterval(
function() {
tverb("UL: " + ulStatus + (graceTimeDone ? "" : " (in grace time)"));
var t = new Date().getTime() - startT;
if (graceTimeDone) ulProgress = (t + bonusT) / (settings.time_ul_max * 1000);
if (t < 200) return;
if (!graceTimeDone) {
if (t > 1000 * settings.time_ulGraceTime) {
if (totLoaded > 0) {
// if the connection is so slow that we didn't get a single chunk yet, do not reset
startT = new Date().getTime();
bonusT = 0;
totLoaded = 0.0;
}
graceTimeDone = true;
}
} else {
var speed = totLoaded / (t / 1000.0);
if (settings.time_auto) {
//decide how much to shorten the test. Every 200ms, the test is shortened by the bonusT calculated here
var bonus = (5.0 * speed) / 100000;
bonusT += bonus > 400 ? 400 : bonus;
}
//update status
ulStatus = ((speed * 8 * settings.overheadCompensationFactor) / (settings.useMebibits ? 1048576 : 1000000)).toFixed(2); // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 or 1000000 to go to megabits/mebibits
if ((t + bonusT) / 1000.0 > settings.time_ul_max || failed) {
// test is over, stop streams and timer
if (failed || isNaN(ulStatus)) ulStatus = "Fail";
clearRequests();
clearInterval(interval);
ulProgress = 1;
tlog("ulTest: " + ulStatus + ", took " + (new Date().getTime() - startT) + "ms");
done();
}
}
}.bind(this),
200
);
}.bind(this);
if (settings.mpot) {
tverb("Sending POST request before performing upload test");
xhr = [];
xhr[0] = new XMLHttpRequest();
xhr[0].onload = xhr[0].onerror = function() {
tverb("POST request sent, starting upload test");
testFunction();
}.bind(this);
xhr[0].open("POST", settings.url_ul) + (settings.mpot ? "cors=true&" : "");
xhr[0].send();
} else testFunction();
}
// ping+jitter test, function done is called when it's over
var ptCalled = false; // used to prevent multiple accidental calls to pingTest
function pingTest(done) {
tverb("pingTest");
if (ptCalled) return;
else ptCalled = true; // pingTest already called?
var startT = new Date().getTime(); //when the test was started
var prevT = null; // last time a pong was received
var ping = 0.0; // current ping value
var jitter = 0.0; // current jitter value
var i = 0; // counter of pongs received
var prevInstspd = 0; // last ping time, used for jitter calculation
xhr = [];
// ping function
var doPing = function() {
tverb("ping");
pingProgress = i / settings.count_ping;
prevT = new Date().getTime();
xhr[0] = new XMLHttpRequest();
xhr[0].onload = function() {
// pong
tverb("pong");
if (i === 0) {
prevT = new Date().getTime(); // first pong
} else {
var instspd = new Date().getTime() - prevT;
if (settings.ping_allowPerformanceApi) {
try {
//try to get accurate performance timing using performance api
var p = performance.getEntries();
p = p[p.length - 1];
var d = p.responseStart - p.requestStart;
if (d <= 0) d = p.duration;
if (d > 0 && d < instspd) instspd = d;
} catch (e) {
//if not possible, keep the estimate
tverb("Performance API not supported, using estimate");
}
}
//noticed that some browsers randomly have 0ms ping
if (instspd < 1) instspd = prevInstspd;
if (instspd < 1) instspd = 1;
var instjitter = Math.abs(instspd - prevInstspd);
if (i === 1) ping = instspd;
/* first ping, can't tell jitter yet*/ else {
if (instspd < ping) ping = instspd; // update ping, if the instant ping is lower
if (i === 2) jitter = instjitter;
//discard the first jitter measurement because it might be much higher than it should be
else jitter = instjitter > jitter ? jitter * 0.3 + instjitter * 0.7 : jitter * 0.8 + instjitter * 0.2; // update jitter, weighted average. spikes in ping values are given more weight.
}
prevInstspd = instspd;
}
pingStatus = ping.toFixed(2);
jitterStatus = jitter.toFixed(2);
i++;
tverb("ping: " + pingStatus + " jitter: " + jitterStatus);
if (i < settings.count_ping) doPing();
else {
// more pings to do?
pingProgress = 1;
tlog("ping: " + pingStatus + " jitter: " + jitterStatus + ", took " + (new Date().getTime() - startT) + "ms");
done();
}
}.bind(this);
xhr[0].onerror = function() {
// a ping failed, cancel test
tverb("ping failed");
if (settings.xhr_ignoreErrors === 0) {
//abort
pingStatus = "Fail";
jitterStatus = "Fail";
clearRequests();
tlog("ping test failed, took " + (new Date().getTime() - startT) + "ms");
pingProgress = 1;
done();
}
if (settings.xhr_ignoreErrors === 1) doPing(); //retry ping
if (settings.xhr_ignoreErrors === 2) {
//ignore failed ping
i++;
if (i < settings.count_ping) doPing();
else {
// more pings to do?
pingProgress = 1;
tlog("ping: " + pingStatus + " jitter: " + jitterStatus + ", took " + (new Date().getTime() - startT) + "ms");
done();
}
}
}.bind(this);
// send xhr
xhr[0].open("GET", settings.url_ping + url_sep(settings.url_ping) + (settings.mpot ? "cors=true&" : "") + "r=" + Math.random(), true); // random string to prevent caching
xhr[0].send();
}.bind(this);
doPing(); // start first ping
}
// telemetry
function sendTelemetry(done) {
if (settings.telemetry_level < 1) return;
xhr = new XMLHttpRequest();
xhr.onload = function() {
try {
var parts = xhr.responseText.split(" ");
if (parts[0] == "id") {
try {
var id = parts[1];
done(id);
} catch (e) {
done(null);
}
} else done(null);
} catch (e) {
done(null);
}
};
xhr.onerror = function() {
console.log("TELEMETRY ERROR " + xhr.status);
done(null);
};
xhr.open("POST", settings.url_telemetry + url_sep(settings.url_telemetry) + (settings.mpot ? "cors=true&" : "") + "r=" + Math.random(), true);
var telemetryIspInfo = {
processedString: clientIp,
rawIspInfo: typeof ispInfo === "object" ? ispInfo : ""
};
try {
var fd = new FormData();
fd.append("ispinfo", JSON.stringify(telemetryIspInfo));
fd.append("dl", dlStatus);
fd.append("ul", ulStatus);
fd.append("ping", pingStatus);
fd.append("jitter", jitterStatus);
fd.append("log", settings.telemetry_level > 1 ? log : "");
fd.append("extra", settings.telemetry_extra);
xhr.send(fd);
} catch (ex) {
var postData = "extra=" + encodeURIComponent(settings.telemetry_extra) + "&ispinfo=" + encodeURIComponent(JSON.stringify(telemetryIspInfo)) + "&dl=" + encodeURIComponent(dlStatus) + "&ul=" + encodeURIComponent(ulStatus) + "&ping=" + encodeURIComponent(pingStatus) + "&jitter=" + encodeURIComponent(jitterStatus) + "&log=" + encodeURIComponent(settings.telemetry_level > 1 ? log : "");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(postData);
}
}

70
includes/CSRF.php Executable file
View File

@@ -0,0 +1,70 @@
<?php
namespace RaspAP\Tokens;
class CSRF
{
protected static ?CSRFTokenizer $instance = null;
/*
* Get the CSRFTokenizer instance (singleton)
*
* @return CSRFTokenizer
*/
public static function instance(): CSRFTokenizer
{
if (self::$instance === null) {
self::$instance = new CSRFTokenizer();
}
return self::$instance;
}
public static function token(): string
{
return self::instance()->getToken();
}
public static function verify(): bool
{
if (!isset($_POST['csrf_token'])) {
return false;
}
return self::instance()->csrfValidateRequest() &&
self::instance()->CSRFValidate($_POST['csrf_token']);
}
public static function metaTag(): string
{
return self::instance()->CSRFMetaTag();
}
public static function hiddenField(): string
{
return self::instance()->CSRFTokenFieldTag();
}
public static function handleInvalidToken(): void
{
self::instance()->handleInvalidCSRFToken();
}
/**
* Validates a CSRF Request
*
* @return bool
*/
public static function validateRequest(): bool
{
$methods = ['POST', 'PUT', 'DELETE', 'PATCH'];
return in_array($_SERVER['REQUEST_METHOD'], $methods) &&
self::instance()->csrfValidateRequest();
}
}
if (\RaspAP\Tokens\CSRF::validateRequest()) {
if (!\RaspAP\Tokens\CSRF::verify()) {
error_log("CSRF verification failed: Token missing or invalid");
\RaspAP\Tokens\CSRF::handleInvalidToken();
}
}

View File

@@ -40,7 +40,6 @@ function DisplayAdBlockConfig()
file_put_contents("/tmp/dnsmasq_custom", $_POST['adblock-custom-hosts'].PHP_EOL);
system("sudo cp /tmp/dnsmasq_custom " .RASPI_ADBLOCK_LISTPATH .'custom.txt', $return);
$config.= 'addn-hosts=' .RASPI_ADBLOCK_LISTPATH .'custom.txt'.PHP_EOL;
$custom_enabled = true;
}
if (empty($errors)) {
@@ -64,6 +63,13 @@ function DisplayAdBlockConfig()
}
}
$custom_list = RASPI_ADBLOCK_LISTPATH . 'custom.txt';
$custom_enabled = false;
if (file_exists($custom_list) && filesize($custom_list) > 0) {
$custom_enabled = true;
}
exec('cat '. RASPI_ADBLOCK_CONFIG, $return);
$arrConf = ParseConfig($return);
if (sizeof($arrConf) > 0) {

View File

@@ -35,6 +35,8 @@ function DisplayAuthConfig($username)
} else {
$status->addMessage('Old password does not match', 'danger');
}
} elseif (isset($_POST['logout'])) {
$auth->logout();
}
echo renderTemplate(

View File

@@ -9,31 +9,13 @@
*/
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = '';
// base directory where all class files are stored
$base_dir = __DIR__ . '/../src/';
// base directory for the namespace prefix
$base_dir = 'src/';
// convert the fully qualified class name into a file path
$file = $base_dir . str_replace('\\', '/', $class) . '.php';
// normalize the base directory with a trailing separator
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
// require the file if it exists
if (file_exists($file)) {
require $file;
}

8
includes/bootstrap.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
// cache-control headers
if (!headers_sent()) {
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
}

View File

@@ -1,6 +1,6 @@
<?php
require_once 'includes/wifi_functions.php';
use RaspAP\Networking\Hotspot\WiFiManager;
/**
*
@@ -8,12 +8,13 @@ require_once 'includes/wifi_functions.php';
*/
function DisplayWPAConfig()
{
$wifi = new WiFiManager();
$status = new \RaspAP\Messages\StatusMessage;
$networks = [];
getWifiInterface();
knownWifiStations($networks);
setKnownStationsWPA($networks);
$wifi->getWifiInterface();
$wifi->knownWifiStations($networks);
$wifi->setKnownStationsWPA($networks);
$iface = escapeshellarg($_SESSION['wifi_client_interface']);
@@ -30,7 +31,7 @@ function DisplayWPAConfig()
} elseif (isset($_POST['wpa_reinit'])) {
$status->addMessage('Attempting to reinitialize wpa_supplicant', 'warning');
$force_remove = true;
$result = reinitializeWPA($force_remove);
$result = $wifi->reinitializeWPA($force_remove);
} elseif (isset($_POST['client_settings'])) {
$tmp_networks = $networks;
if ($wpa_file = fopen('/tmp/wifidata', 'w')) {
@@ -90,7 +91,7 @@ function DisplayWPAConfig()
if (strlen($network['passphrase']) >=8 && strlen($network['passphrase']) <= 63) {
unset($wpa_passphrase);
unset($line);
exec('wpa_passphrase '. ssid2utf8( escapeshellarg($ssid) ) . ' ' . escapeshellarg($network['passphrase']), $wpa_passphrase);
exec('wpa_passphrase '. $wifi->ssid2utf8( escapeshellarg($ssid) ) . ' ' . escapeshellarg($network['passphrase']), $wpa_passphrase);
foreach ($wpa_passphrase as $line) {
if (preg_match('/^\s*}\s*$/', $line)) {
if (array_key_exists('priority', $network)) {

View File

@@ -1,7 +0,0 @@
<?php
require_once 'functions.php';
if (csrfValidateRequest() && !CSRFValidate()) {
handleInvalidCSRFToken();
}

View File

@@ -1,230 +1,180 @@
<?php
require_once 'includes/config.php';
require_once 'includes/wifi_functions.php';
require_once 'includes/functions.php';
use RaspAP\System\Sysinfo;
use RaspAP\UI\Dashboard;
use RaspAP\Messages\StatusMessage;
use RaspAP\Plugins\PluginManager;
use RaspAP\Networking\Hotspot\WiFiManager;
/**
* Show dashboard page.
* Displays the dashboard
*/
function DisplayDashboard(&$extraFooterScripts)
function DisplayDashboard(&$extraFooterScripts): void
{
getWifiInterface();
$status = new \RaspAP\Messages\StatusMessage;
// Need this check interface name for proper shell execution.
if (!preg_match('/^([a-zA-Z0-9]+)$/', $_SESSION['wifi_client_interface'])) {
$status->addMessage(_('Interface name invalid.'), 'danger');
$status->showMessages();
return;
}
// instantiate RaspAP objects
$system = new Sysinfo();
$dashboard = new Dashboard();
$status = new StatusMessage();
$pluginManager = PluginManager::getInstance();
$wifi = new WiFiManager();
if (!function_exists('exec')) {
$status->addMessage(_('Required exec function is disabled. Check if exec is not added to php disable_functions.'), 'danger');
$status->showMessages();
return;
}
exec('ip a show '.$_SESSION['ap_interface'], $stdoutIp);
$stdoutIpAllLinesGlued = implode(" ", $stdoutIp);
$stdoutIpWRepeatedSpaces = preg_replace('/\s\s+/', ' ', $stdoutIpAllLinesGlued);
// set AP and client interface session vars
$wifi->getWifiInterface();
preg_match('/link\/ether ([0-9a-f:]+)/i', $stdoutIpWRepeatedSpaces, $matchesMacAddr) || $matchesMacAddr[1] = _('No MAC Address Found');
$macAddr = $matchesMacAddr[1];
$ipv4Addrs = '';
$ipv4Netmasks = '';
if (!preg_match_all('/inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/([0-3][0-9])/i', $stdoutIpWRepeatedSpaces, $matchesIpv4AddrAndSubnet, PREG_SET_ORDER)) {
$ipv4Addrs = _('No IPv4 Address Found');
} else {
foreach ($matchesIpv4AddrAndSubnet as $inet) {
$address = $inet[1];
$suffix = (int) $inet[2];
$netmask = long2ip(-1 << (32 - $suffix));
$ipv4Addrs .= " $address";
$ipv4Netmasks .= " $netmask";
}
$ipv4Addrs = trim($ipv4Addrs);
$ipv4Netmasks = trim($ipv4Netmasks);
}
$ipv4Netmasks = empty($ipv4Netmasks) ? "-" : $ipv4Netmasks;
$ipv6Addrs = '';
if (!preg_match_all('/inet6 ([a-f0-9:]+)/i', $stdoutIpWRepeatedSpaces, $matchesIpv6Addr)) {
$ipv6Addrs = _('No IPv6 Address Found');
} else {
if (isset($matchesIpv6Addr[1])) {
$ipv6Addrs = implode(' ', $matchesIpv6Addr[1]);
}
}
preg_match('/state (UP|DOWN)/i', $stdoutIpWRepeatedSpaces, $matchesState) || $matchesState[1] = 'unknown';
$interfaceState = $matchesState[1];
// Because of table layout used in the ip output we get the interface statistics directly from
// the system. One advantage of this is that it could work when interface is disable.
exec('cat /sys/class/net/'.$_SESSION['ap_interface'].'/statistics/rx_packets ', $stdoutCatRxPackets);
$strRxPackets = _('No data');
if (ctype_digit($stdoutCatRxPackets[0])) {
$strRxPackets = $stdoutCatRxPackets[0];
}
exec('cat /sys/class/net/'.$_SESSION['ap_interface'].'/statistics/tx_packets ', $stdoutCatTxPackets);
$strTxPackets = _('No data');
if (ctype_digit($stdoutCatTxPackets[0])) {
$strTxPackets = $stdoutCatTxPackets[0];
}
exec('cat /sys/class/net/'.$_SESSION['ap_interface'].'/statistics/rx_bytes ', $stdoutCatRxBytes);
$strRxBytes = _('No data');
if (ctype_digit($stdoutCatRxBytes[0])) {
$strRxBytes = $stdoutCatRxBytes[0];
$strRxBytes .= getHumanReadableDatasize($strRxBytes);
}
exec('cat /sys/class/net/'.$_SESSION['ap_interface'].'/statistics/tx_bytes ', $stdoutCatTxBytes);
$strTxBytes = _('No data');
if (ctype_digit($stdoutCatTxBytes[0])) {
$strTxBytes = $stdoutCatTxBytes[0];
$strTxBytes .= getHumanReadableDatasize($strTxBytes);
}
exec ('vnstat --dbiflist', $stdoutVnStatDB);
if (!preg_match('/'.$_SESSION['ap_interface'].'/', $stdoutVnStatDB[0])) {
exec('sudo vnstat --add --iface '.$_SESSION['ap_interface'], $return);
}
define('SSIDMAXLEN', 32);
// Warning iw comes with: "Do NOT screenscrape this tool, we don't consider its output stable."
exec('iw dev ' .$_SESSION['wifi_client_interface']. ' link ', $stdoutIw);
$stdoutIwAllLinesGlued = implode('+', $stdoutIw); // Break lines with character illegal in SSID and MAC addr
$stdoutIwWRepSpaces = preg_replace('/\s\s+/', ' ', $stdoutIwAllLinesGlued);
preg_match('/Connected to (([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2}))/', $stdoutIwWRepSpaces, $matchesBSSID) || $matchesBSSID[1] = '';
$connectedBSSID = $matchesBSSID[1];
$connectedBSSID = empty($connectedBSSID) ? "-" : $connectedBSSID;
$wlanHasLink = false;
if ($interfaceState === 'UP') {
$wlanHasLink = true;
}
if (!preg_match('/SSID: ([^+]{1,'.SSIDMAXLEN.'})/', $stdoutIwWRepSpaces, $matchesSSID)) {
$wlanHasLink = false;
$matchesSSID[1] = 'None';
}
$connectedSSID = str_replace('\x20', '', $matchesSSID[1]);
preg_match('/freq: (\d+)/i', $stdoutIwWRepSpaces, $matchesFrequency) || $matchesFrequency[1] = '';
$frequency = $matchesFrequency[1].' MHz';
preg_match('/signal: (-?[0-9]+ dBm)/i', $stdoutIwWRepSpaces, $matchesSignal) || $matchesSignal[1] = '';
$signalLevel = $matchesSignal[1];
$signalLevel = empty($signalLevel) ? "-" : $signalLevel;
preg_match('/tx bitrate: ([0-9\.]+ [KMGT]?Bit\/s)/', $stdoutIwWRepSpaces, $matchesBitrate) || $matchesBitrate[1] = '';
$bitrate = $matchesBitrate[1];
$bitrate = empty($bitrate) ? "-" : $bitrate;
// txpower is now displayed on iw dev(..) info command, not on link command.
exec('iw dev '.$_SESSION['wifi_client_interface'].' info ', $stdoutIwInfo);
$stdoutIwInfoAllLinesGlued = implode(' ', $stdoutIwInfo);
$stdoutIpInfoWRepSpaces = preg_replace('/\s\s+/', ' ', $stdoutIwInfoAllLinesGlued);
preg_match('/txpower ([0-9\.]+ dBm)/i', $stdoutIpInfoWRepSpaces, $matchesTxPower) || $matchesTxPower[1] = '';
$txPower = $matchesTxPower[1];
// iw does not have the "Link Quality". This is a is an aggregate value,
// and depends on the driver and hardware.
// Display link quality as signal quality for now.
$strLinkQuality = 0;
if ($signalLevel > -100 && $wlanHasLink) {
if ($signalLevel >= 0) {
$strLinkQuality = 100;
} else {
$strLinkQuality = 100 + intval($signalLevel);
}
}
$wlan0up = false;
$classMsgDevicestatus = 'warning';
if ($interfaceState === 'UP') {
$wlan0up = true;
$classMsgDevicestatus = 'success';
}
if (!RASPI_MONITOR_ENABLED) {
if (isset($_POST['ifdown_wlan0'])) {
// Pressed stop button
if ($interfaceState === 'UP') {
$status->addMessage(sprintf(_('Interface is going %s.'), _('down')), 'warning');
exec('sudo ip link set '.$_SESSION['ap_interface'].' down');
$wlan0up = false;
$status->addMessage(sprintf(_('Interface is now %s.'), _('down')), 'success');
} elseif ($interfaceState === 'unknown') {
$status->addMessage(_('Interface state unknown.'), 'danger');
} else {
$status->addMessage(sprintf(_('Interface already %s.'), _('down')), 'warning');
}
} elseif (isset($_POST['ifup_wlan0'])) {
// Pressed start button
if ($interfaceState === 'DOWN') {
$status->addMessage(sprintf(_('Interface is going %s.'), _('up')), 'warning');
exec('sudo ip link set ' .$_SESSION['ap_interface']. ' up');
exec('sudo ip -s a f label ' .$_SESSION['ap_interface']);
$wlan0up = true;
$status->addMessage(sprintf(_('Interface is now %s.'), _('up')), 'success');
} elseif ($interfaceState === 'unknown') {
$status->addMessage(_('Interface state unknown.'), 'danger');
} else {
$status->addMessage(sprintf(_('Interface already %s.'), _('up')), 'warning');
}
} else {
$status->addMessage(sprintf(_('Interface is %s.'), strtolower($interfaceState)), $classMsgDevicestatus);
}
}
// brought in from template
$arrHostapdConf = parse_ini_file(RASPI_CONFIG.'/hostapd.ini');
$bridgedEnable = $arrHostapdConf['BridgedEnable'];
$interface = $_SESSION['ap_interface'] ?? 'wlan0';
$clientInterface = $_SESSION['wifi_client_interface'];
$apInterface = $_SESSION['ap_interface'];
$MACPattern = '"([[:xdigit:]]{2}:){5}[[:xdigit:]]{2}"';
$hostname = $system->hostname();
$revision = $system->rpiRevision();
$deviceImage = $dashboard->getDeviceImage($revision);
$hostapd = $system->hostapdStatus();
$adblock = $system->adBlockStatus();
$vpn = $system->getActiveVpnInterface();
$frequency = $dashboard->getFrequencyBand($interface);
$details = $dashboard->getInterfaceDetails($interface);
$wireless = $dashboard->getWirelessDetails($interface);
$connectionType = $dashboard->getConnectionType();
$connectionIcon = $dashboard->getConnectionIcon($connectionType);
$state = strtolower($details['state']);
$wirelessClients = $dashboard->getWirelessClients($interface);
$ethernetClients = $dashboard->getEthernetClients();
$totalClients = $wirelessClients + $ethernetClients;
$plugins = $pluginManager->getInstalledPlugins();
$bridgedEnable = getBridgedState();
if (getBridgedState()) {
$moreLink = "hostapd_conf";
exec('iw dev ' . $apInterface . ' station dump | grep -oE ' . $MACPattern, $clients);
} else {
$moreLink = "dhcpd_conf";
exec('cat ' . RASPI_DNSMASQ_LEASES . '| grep -E $(iw dev ' . $apInterface . ' station dump | grep -oE ' . $MACPattern . ' | paste -sd "|")', $clients);
// handle page actions
if (!empty($_POST)) {
$status = $dashboard->handlePageAction($state, $_POST, $status, $interface);
// refresh interface details + state
$details = $dashboard->getInterfaceDetails($interface);
$state = strtolower($details['state']);
}
$ipv4Address = $details['ipv4'];
$ipv4Netmask = $details['ipv4_netmask'];
$macAddress = $details['mac'];
$ssid = $wireless['ssid'];
$ethernetActive = ($connectionType === 'ethernet') ? "active" : "inactive";
$wirelessActive = ($connectionType === 'wireless') ? "active" : "inactive";
$tetheringActive = ($connectionType === 'tethering') ? "active" : "inactive";
$cellularActive = ($connectionType === 'cellular') ? "active" : "inactive";
$bridgedStatus = ($bridgedEnable == 1) ? "active" : "";
$hostapdStatus = ($hostapd[0] == 1) ? "active" : "";
$adblockStatus = ($adblock == true) ? "active" : "";
$wirelessClientActive = ($wirelessClients > 0) ? "active" : "inactive";
$wirelessClientLabel = sprintf(
_('%d WLAN %s'),
$wirelessClients,
$dashboard->formatClientLabel($wirelessClients)
);
$ethernetClientActive = ($ethernetClients > 0) ? "active" : "inactive";
$ethernetClientLabel = sprintf(
_('%d LAN %s'),
$ethernetClients,
$dashboard->formatClientLabel($ethernetClients)
);
$totalClientsActive = ($totalClients > 0) ? "active": "inactive";
$freq5active = $freq24active = "";
$varName = "freq" . str_replace('.', '', $frequency) . "active";
$$varName = "active";
$vpnStatus = $vpn ? "active" : "inactive";
$vpnManaged = $vpn ? $dashboard->getVpnManaged($vpn) : null;
$firewallManaged = $firewallStatus = "";
$firewallInstalled = array_filter($plugins, fn($p) => str_ends_with($p, 'Firewall')) ? true : false;
if (!$firewallInstalled) {
$firewallUnavailable = '<i class="fas fa-slash fa-stack-1x"></i>';
} else {
$firewallManaged = '<a href="/plugin__Firewall">';
$firewallStatus = ($dashboard->firewallEnabled() == true) ? "active" : "";
}
$ifaceStatus = $wlan0up ? "up" : "down";
echo renderTemplate(
"dashboard", compact(
"clients",
"moreLink",
"apInterface",
"revision",
"deviceImage",
"interface",
"clientInterface",
"ifaceStatus",
"bridgedEnable",
"status",
"ipv4Addrs",
"ipv4Netmasks",
"ipv6Addrs",
"macAddr",
"strRxPackets",
"strRxBytes",
"strTxPackets",
"strTxBytes",
"connectedSSID",
"connectedBSSID",
"bitrate",
"signalLevel",
"txPower",
"state",
"bridgedStatus",
"hostapdStatus",
"adblockStatus",
"vpnStatus",
"vpnManaged",
"firewallUnavailable",
"firewallStatus",
"firewallManaged",
"ipv4Address",
"ipv4Netmask",
"macAddress",
"ssid",
"frequency",
"strLinkQuality",
"wlan0up"
"freq5active",
"freq24active",
"wirelessClients",
"wirelessClientLabel",
"wirelessClientActive",
"ethernetClients",
"ethernetClientLabel",
"ethernetClientActive",
"totalClients",
"totalClientsActive",
"connectionType",
"connectionIcon",
"ethernetActive",
"wirelessActive",
"tetheringActive",
"cellularActive",
"status"
)
);
$extraFooterScripts[] = array('src'=>'app/js/dashboardchart.js', 'defer'=>false);
$extraFooterScripts[] = array('src'=>'app/js/linkquality.js', 'defer'=>false);
$extraFooterScripts[] = array('src'=>'app/js/vendor/dashboardchart.js', 'defer'=>false);
}
/**
* Renders a URL for an svg solid line representing the associated
* connection type
*
* @param string $connectionType
* @return string
*/
function renderConnection(string $connectionType): string
{
$deviceMap = [
'ethernet' => 'device-1',
'wireless' => 'device-2',
'tethering' => 'device-3',
'cellular' => 'device-4'
];
$device = $deviceMap[$connectionType] ?? 'device-unknown';
// return generated URL for solid.php
return sprintf('app/img/solid.php?joint&%s&out', $device);
}
/**
* Renders a URL for an svg solid line representing associated
* client connection(s)
*
* @param int $wirelessClients
* @param int $ethernetClients
* @return string
*/
function renderClientConnections(int $wirelessClients, int $ethernetClients): string
{
$devices = [];
if ($wirelessClients > 0) {
$devices[] = 'device-1&out';
}
if ($ethernetClients > 0) {
$devices[] = 'device-2&out';
}
return empty($devices) ? '' : sprintf(
'<img src="app/img/right-solid.php?%s" class="solid-lines solid-lines-right" alt="Client connections">',
implode('&', $devices)
);
}

View File

@@ -9,5 +9,5 @@ function DisplayDataUsage(&$extraFooterScripts)
echo renderTemplate("data_usage", [ "interfaces" => $interfacesWlo ]);
$extraFooterScripts[] = array('src'=>'dist/datatables/jquery.dataTables.min.js', 'defer'=>false);
$extraFooterScripts[] = array('src'=>'app/js/bandwidthcharts.js', 'defer'=>false);
$extraFooterScripts[] = array('src'=>'app/js/vendor/bandwidthcharts.js', 'defer'=>false);
}

View File

@@ -7,7 +7,7 @@ if (!defined('RASPI_CONFIG')) {
$defaults = [
'RASPI_BRAND_TEXT' => 'RaspAP',
'RASPI_BRAND_TITLE' => RASPI_BRAND_TEXT.' Admin Panel',
'RASPI_VERSION' => '3.2.6',
'RASPI_VERSION' => '3.4.0',
'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json',
'RASPI_CONFIG_PROVIDERS' => 'config/vpn-providers.json',
'RASPI_CONFIG_API' => RASPI_CONFIG.'/api',
@@ -37,11 +37,17 @@ $defaults = [
'RASPI_OPENVPN_CLIENT_LOGIN' => '/etc/openvpn/client/login.conf',
'RASPI_WIREGUARD_PATH' => '/etc/wireguard/',
'RASPI_WIREGUARD_CONFIG' => RASPI_WIREGUARD_PATH.'wg0.conf',
'RASPI_IPTABLES_CONF' => RASPI_CONFIG.'/networking/iptables_rules.json',
'RASPI_TORPROXY_ENABLED' => false,
'RASPI_TORPROXY_CONFIG' => '/etc/tor/torrc',
'RASPI_LIGHTTPD_CONFIG' => '/etc/lighttpd/lighttpd.conf',
'RASPI_ACCESS_CHECK_IP' => '1.1.1.1',
'RASPI_ACCESS_CHECK_DNS' => 'one.one.one.one',
// Captive portal detection - returns 204 or 200 is successful
'RASPI_ACCESS_CHECK_URL' => 'http://detectportal.firefox.com',
'RASPI_ACCESS_CHECK_URL_CODE' => 200,
// Constants for the 5GHz wireless regulatory domain
'RASPI_5GHZ_CHANNEL_MIN' => 100,
'RASPI_5GHZ_CHANNEL_MAX' => 192,
@@ -58,7 +64,6 @@ $defaults = [
'RASPI_OPENVPN_ENABLED' => false,
'RASPI_VPN_PROVIDER_ENABLED' => false,
'RASPI_WIREGUARD_ENABLED' => false,
'RASPI_TORPROXY_ENABLED' => false,
'RASPI_CONFAUTH_ENABLED' => true,
'RASPI_CHANGETHEME_ENABLED' => true,
'RASPI_VNSTAT_ENABLED' => true,

Some files were not shown because too many files have changed in this diff Show More