├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── issue_form.yml └── workflows │ ├── codeql-analysis.yml │ ├── codesee-arch-diagram.yml │ ├── release.yml │ └── stale.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── BACKERS.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── _config.yml ├── _layouts └── default.html ├── ajax ├── adblock │ └── update_blocklist.php ├── bandwidth │ ├── get_bandwidth.php │ └── get_bandwidth_hourly.php ├── logging │ └── clearlog.php ├── networking │ ├── do_sys_reset.php │ ├── get_all_interfaces.php │ ├── get_channel.php │ ├── get_frequencies.php │ ├── get_ip_summary.php │ ├── get_netcfg.php │ ├── get_nl80211_band.php │ ├── get_wgcfg.php │ ├── get_wgkey.php │ └── wifi_stations.php ├── openvpn │ ├── activate_ovpncfg.php │ └── del_ovpncfg.php ├── plugins │ └── do_plugin_install.php ├── session │ └── do_check_session.php └── system │ ├── sys_actions.php │ ├── sys_chk_update.php │ ├── sys_debug.php │ ├── sys_get_logfile.php │ ├── sys_perform_update.php │ └── sys_read_logfile.php ├── api ├── auth.py ├── main.py ├── modules │ ├── ap.py │ ├── client.py │ ├── ddns.py │ ├── dhcp.py │ ├── dns.py │ ├── firewall.py │ ├── networking.py │ ├── openvpn.py │ ├── system.py │ └── wireguard.py └── requirements.txt ├── app ├── css │ ├── all.css │ ├── custom.php │ ├── dark.css │ └── hackernews.css ├── icons │ ├── apple-touch-icon.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── favicon.svg │ ├── site.webmanifest │ ├── web-app-manifest-192x192.png │ └── web-app-manifest-512x512.png ├── img │ ├── dashed.svg │ ├── devices │ │ ├── compute.php │ │ ├── default.php │ │ └── zero.php │ ├── insiders.png │ ├── raspAP-logo.php │ ├── raspAP-logo.png │ ├── right-dashed.svg │ ├── right-solid.php │ ├── solid.php │ ├── uri-qr-code.php │ ├── wg-qr-code.php │ └── wifi-qr-code.php ├── js │ ├── bandwidthcharts.js │ ├── bandwidthcharts.min.js │ ├── custom.js │ ├── custom.min.js │ ├── dashboardchart.js │ ├── huebee.js │ └── linkquality.js ├── lib │ └── Parsedown.php └── pitft │ └── stats.py ├── composer.json ├── config ├── 090_raspap.conf ├── 090_wlan0.conf ├── 50-raspap-router.conf ├── blocklists.json ├── client_config │ ├── 70-mobile-data-sticks.rules │ ├── 80-raspap-net-devices.rules │ ├── huawei_hilink_api.sh │ ├── info_huawei.sh │ ├── info_huawei_hilink.sh │ ├── info_huawei_modem.sh │ ├── interfaces │ ├── mcc-mnc-table.csv │ ├── onoff_huawei_hilink.sh │ ├── ppp0_route.sh │ ├── ppp0_setpin.sh │ ├── raspap_helpers.sh │ ├── start_huawei_hilink@.service │ ├── start_ppp0_device.service │ └── wvdial.conf ├── client_udev_prototypes.json ├── config.php ├── defaults.json ├── dhcpcd.conf ├── dns-servers.json ├── hostapd.conf ├── iptables_rules.json ├── raspap-br0-member-eth0.network ├── raspap-bridge-br0.netdev ├── raspap-bridge-br0.netplan └── vpn-providers.json ├── crowdin.yml ├── dist ├── bootstrap │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ └── bootstrap.min.css │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map ├── chart.js │ ├── Chart.bundle.js │ ├── Chart.bundle.min.js │ ├── Chart.js │ └── Chart.min.js ├── datatables │ ├── dataTables.bootstrap4.css │ ├── dataTables.bootstrap4.js │ ├── dataTables.bootstrap4.min.css │ ├── dataTables.bootstrap4.min.js │ ├── jquery.dataTables.js │ └── jquery.dataTables.min.js ├── font-awesome │ ├── LICENSE.txt │ ├── css │ │ ├── all.css │ │ ├── all.min.css │ │ ├── brands.css │ │ ├── brands.min.css │ │ ├── fontawesome.css │ │ ├── fontawesome.min.css │ │ ├── regular.css │ │ ├── regular.min.css │ │ ├── solid.css │ │ ├── solid.min.css │ │ ├── svg-with-js.css │ │ ├── svg-with-js.min.css │ │ ├── v4-font-face.css │ │ ├── v4-font-face.min.css │ │ ├── v4-shims.css │ │ ├── v4-shims.min.css │ │ ├── v5-font-face.css │ │ └── v5-font-face.min.css │ ├── package.json │ └── webfonts │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.svg │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.svg │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.svg │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ ├── fa-solid-900.woff2 │ │ ├── fa-v4compatibility.ttf │ │ └── fa-v4compatibility.woff2 ├── huebee │ ├── huebee.css │ ├── huebee.min.css │ ├── huebee.pkgd.js │ └── huebee.pkgd.min.js ├── jquery-easing │ ├── jquery.easing.compatibility.js │ ├── jquery.easing.js │ └── jquery.easing.min.js ├── jquery-mask │ └── jquery.mask.min.js ├── jquery │ ├── jquery.js │ ├── jquery.min.js │ ├── jquery.min.map │ ├── jquery.slim.js │ ├── jquery.slim.min.js │ └── jquery.slim.min.map ├── raspap │ └── css │ │ ├── fonts │ │ ├── RaspAP.eot │ │ ├── RaspAP.svg │ │ ├── RaspAP.ttf │ │ └── RaspAP.woff │ │ └── style.css └── sb-admin │ ├── css │ └── styles.css │ └── js │ └── scripts.js ├── favicon.ico ├── gulpfile.js ├── includes ├── CSRF.php ├── about.php ├── adblock.php ├── admin.php ├── authenticate.php ├── autoload.php ├── configure_client.php ├── dashboard.php ├── data_usage.php ├── defaults.php ├── dhcp.php ├── footer.php ├── functions.php ├── get_clients.php ├── hostapd.php ├── internetRoute.php ├── locale.php ├── login.php ├── navbar.php ├── networking.php ├── openvpn.php ├── page_actions.php ├── provider.php ├── restapi.php ├── session.php ├── sidebar.php ├── sysstats.php ├── system.php ├── wifi_functions.php └── wireguard.php ├── index.php ├── installers ├── common.sh ├── configauth.sh ├── configport.sh ├── debuglog.sh ├── dhcpcd.service ├── disablelog.sh ├── enablelog.sh ├── install_feature_clients.sh ├── install_feature_firewall.sh ├── minwrite.sh ├── mkcert.sh ├── openvpnlog.sh ├── plugin_helper.sh ├── raspap-network-activity@.service ├── raspap-network-monitor.c ├── raspap.sudoers ├── raspapd.service ├── raspbian.sh ├── restapi.service ├── servicestart.sh ├── toggle-bridged-routed.sh ├── uninstall.sh ├── update_blocklist.sh └── update_firewall.sh ├── locale ├── cs_CZ │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── da_DK │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── de_DE │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── el_GR │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── en_US │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── es_MX │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── fi_FI │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── fr_FR │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── id_ID │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── it_IT │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── ja_JP │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── ko_KR │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── nl_NL │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── pl_PL │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── pocompile.sh ├── pt_BR │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── ro_RO │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── ru_RU │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── sk_SK │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── sv_SE │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── tr_TR │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── vi_VN │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po ├── zh_CN │ └── LC_MESSAGES │ │ ├── messages.mo │ │ └── messages.po └── zh_TW │ └── LC_MESSAGES │ ├── messages.mo │ └── messages.po ├── package.json ├── src └── RaspAP │ ├── Auth │ └── HTTPAuth.php │ ├── DotEnv │ └── DotEnv.php │ ├── Exceptions │ ├── ExceptionHandler.php │ └── HtmlErrorRenderer.php │ ├── Messages │ └── StatusMessage.php │ ├── Parsers │ └── IwParser.php │ ├── Plugins │ ├── PluginInstaller.php │ ├── PluginInterface.php │ └── PluginManager.php │ ├── System │ └── Sysinfo.php │ ├── Tokens │ └── CSRFTokenizer.php │ ├── UI │ ├── Dashboard.php │ └── Sidebar.php │ └── Uploader │ └── FileUpload.php ├── templates ├── about.php ├── about │ ├── contributing.php │ ├── general.php │ └── insiders.php ├── adblock.php ├── adblock │ ├── custom.php │ ├── general.php │ ├── logging.php │ └── stats.php ├── admin.php ├── configure_client.php ├── dashboard.php ├── dashboard │ ├── data.php │ └── status.php ├── data_usage.php ├── dhcp.php ├── dhcp │ ├── advanced.php │ ├── clients.php │ ├── general.php │ ├── logging.php │ └── static_leases.php ├── exception.php ├── hostapd.php ├── hostapd │ ├── advanced.php │ ├── basic.php │ ├── logging.php │ └── security.php ├── login.php ├── networking.php ├── openvpn.php ├── openvpn │ ├── configs.php │ ├── general.php │ └── logging.php ├── provider.php ├── provider │ ├── general.php │ └── status.php ├── restapi.php ├── restapi │ ├── general.php │ └── status.php ├── system.php ├── system │ ├── advanced.php │ ├── basic.php │ ├── language.php │ ├── plugins.php │ ├── theme.php │ └── tools.php ├── torproxy.php ├── wg │ ├── general.php │ ├── logging.php │ └── peers.php ├── wifi_stations.php ├── wifi_stations │ └── network.php └── wireguard.php └── yarn.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: RaspAP 2 | 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: RaspAP Community Support 4 | url: https://github.com/RaspAP/raspap-webgui/discussions 5 | about: Please ask and answer questions here. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Is your feature request related to a problem? 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | ## Describe the solution you'd like 14 | A clear and concise description of what you want to happen. 15 | 16 | ## Describe alternatives you've considered 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | ## Additional context 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '17 9 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript', 'python' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/codesee-arch-diagram.yml: -------------------------------------------------------------------------------- 1 | # This workflow was added by CodeSee. Learn more at https://codesee.io/ 2 | # This is v2.0 of this workflow file 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request_target: 8 | types: [opened, synchronize, reopened] 9 | 10 | name: CodeSee 11 | 12 | permissions: read-all 13 | 14 | jobs: 15 | codesee: 16 | runs-on: ubuntu-latest 17 | continue-on-error: true 18 | name: Analyze the repo with CodeSee 19 | steps: 20 | - uses: Codesee-io/codesee-action@v2 21 | with: 22 | codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} 23 | codesee-url: https://app.codesee.io 24 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v8.0.0 14 | with: 15 | days-before-issue-stale: 30 16 | days-before-issue-close: 14 17 | stale-issue-label: "stale" 18 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." 19 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 20 | exempt-issue-labels: pinned, enhancement, feature request 21 | days-before-pr-stale: -1 22 | days-before-pr-close: -1 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | yarn-error.log 4 | *.swp 5 | includes/config.php 6 | rootCA.pem 7 | vendor 8 | .env 9 | locale/**/*.mo 10 | app/net_activity 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "plugins"] 2 | path = plugins 3 | url = https://github.com/RaspAP/plugins 4 | branch = master 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: php 3 | 4 | matrix: 5 | fast_finish: true 6 | 7 | include: 8 | - php: '7.0' 9 | - php: '7.1' 10 | - php: '7.2' 11 | - php: '7.3' 12 | - php: '7.4' 13 | - php: 'nightly' 14 | 15 | allow_failures: 16 | - php: nightly 17 | 18 | before_script: 19 | - composer install --prefer-source --quiet --no-interaction 20 | 21 | # Run test script commands. 22 | script: 23 | - composer test 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute 2 | Everyone is invited and welcome to contribute to RaspAP. There's a lot to do — if you're not a developer we can always use help with the official [project documentation](https://docs.raspap.com) and language [translations](https://crowdin.com/project/raspap). If you have experience in Linux networking, you can share your knowledge by answering questions from RaspAP users in our [GitHub discussions](https://github.com/RaspAP/raspap-webgui/discussions) and/or the [/r/RaspAP](https://reddit.com/r/RaspAP) subreddit. 3 | 4 | If you're a devloper, the process of contributing code is straightforward: 5 | 6 | 1. Fork the project in your account and create a new branch: `feat/your-feature` or `fix/your-bugfix`. 7 | 2. Open [an issue](https://github.com/RaspAP/raspap-webgui/issues) describing the feature or bug fix contribution you'd like to make. 8 | 3. Commit changes in your branch. 9 | 4. Open a pull request and reference the initial issue in the pull request message, or by linking it in GitHub. 10 | 11 | ### Coding standards 12 | This project follows the [PSR-2](http://www.php-fig.org/psr/psr-2/) coding style guidelines. There are many ways to check your code for PSR-2. An excellent tool is [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer). The command line tool phpcs can be run against any single file. [Phing](https://www.phing.info/), a PHP build tool, integrates nicely with `phpcs` to automate PSR-2 checks across all source files in a project. 13 | 14 | ### RaspAP community 15 | RaspAP is made possible by a strong [community of developers](https://github.com/RaspAP/raspap-webgui/graphs/contributors). If you have any questions or would like to get involved in RaspAP, dive into any of these channels: 16 | 17 | * [GitHub discussions](https://github.com/RaspAP/raspap-webgui/discussions) 18 | * [Discord chat](https://discord.gg/KVAsaAR) 19 | * [Twitter](https://twitter.com/rasp_ap) 20 | * [Reddit](https://www.reddit.com/r/RaspAP/) 21 | 22 | If you enjoy using RaspAP and would like to support our work financially, consider becoming an [Insider](https://github.com/sponsors/RaspAP). 23 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | The RaspAP team and community take all security vulnerabilities seriously. This document outlines security procedures and general policies for the RaspAP open source projects as found on https://github.com/RaspAP/. 4 | If you believe you have found a security vulnerability in any RaspAP-owned repository, please report it to us as described below. 5 | 6 | ## Reporting a vulnerability in RaspAP 7 | 8 | Thank you for improving the security of our open source software. 9 | We appreciate your efforts and responsible disclosure, and will make every effort to acknowledge your contributions. 10 | 11 | Please report (suspected) security vulnerabilities to [security@raspap.com](mailto:security@raspap.com). The requested information listed below will help us better understand the nature and scope of the possible issue: 12 | 13 | 1. Type of issue (eg. shell exploit, cross-site scripting, etc.) 14 | 2. Full paths of source file(s) related to the manifestation of the issue 15 | 3. The location of the affected source code (tag/branch/commit or direct URL) 16 | 4. Any special configuration required to reproduce the issue 17 | 5. Step-by-step instructions to reproduce the issue 18 | 6. Proof-of-concept or exploit code (if possible) 19 | 7. Impact of the issue, including how an attacker might exploit the issue 20 | 21 | This information will help us triage your report more quickly. 22 | 23 | You will receive a response from us within 48 hours. Developers may ask for additional information or clarity on your report. 24 | If the issue is confirmed, we will release a patch as soon as possible depending on complexity, but historically within a few days. 25 | 26 | ## Third-party modules 27 | Report security vulnerabilities in third-party modules to the person or team maintaining the module. 28 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /ajax/adblock/update_blocklist.php: -------------------------------------------------------------------------------- 1 | $return,'list'=>$dest]; 50 | echo json_encode($jsonData); 51 | } else { 52 | $jsonData = ['return'=>2,'output'=>['Error getting data']]; 53 | echo json_encode($jsonData); 54 | } 55 | -------------------------------------------------------------------------------- /ajax/bandwidth/get_bandwidth.php: -------------------------------------------------------------------------------- 1 | 0) { 13 | $interface = $interfacesWlo[0]; 14 | } else { 15 | exit('No network interfaces found.'); 16 | } 17 | } 18 | define('IFNAMSIZ', 16); 19 | if (strlen($interface) > IFNAMSIZ) { 20 | exit('Interface name too long.'); 21 | } elseif(!preg_match('/^[a-zA-Z0-9]+$/', $interface)) { 22 | exit('Invalid interface name.'); 23 | } 24 | 25 | require_once './get_bandwidth_hourly.php'; 26 | 27 | exec( 28 | sprintf('vnstat -i %s --json ', escapeshellarg($interface)), $jsonstdoutvnstat, 29 | $exitcodedaily 30 | ); 31 | if ($exitcodedaily !== 0) { 32 | exit('vnstat error'); 33 | } 34 | 35 | $jsonobj = json_decode($jsonstdoutvnstat[0], true); 36 | $timeunits = filter_input(INPUT_GET, 'tu'); 37 | if ($timeunits === 'm') { 38 | // months 39 | $jsonData = $jsonobj['interfaces'][0]['traffic']['month']; 40 | } else { 41 | // default: days 42 | $jsonData = $jsonobj['interfaces'][0]['traffic']['day']; 43 | } 44 | 45 | $datasizeunits = filter_input(INPUT_GET, 'dsu'); 46 | $dsu_factor = $datasizeunits == "mb" ? 1024 * 1024 : 1024; 47 | header('X-Content-Type-Options: nosniff'); 48 | header('Content-Type: application/json'); 49 | echo '[ '; 50 | $firstelm = true; 51 | for ($i = count($jsonData) - 1; $i >= 0; --$i) { 52 | if ($timeunits === 'm') { 53 | $dt = DateTime::createFromFormat( 54 | 'Y n', $jsonData[$i]['date']['year'].' '. 55 | $jsonData[$i]['date']['month'] 56 | ); 57 | } else { 58 | $dt = DateTime::createFromFormat( 59 | 'Y n j', $jsonData[$i]['date']['year'].' '. 60 | $jsonData[$i]['date']['month'].' '. 61 | $jsonData[$i]['date']['day'] 62 | ); 63 | } 64 | 65 | if ($firstelm) { 66 | $firstelm = false; 67 | } else { 68 | echo ','; 69 | } 70 | 71 | $datasend = round($jsonData[$i]['tx'] / $dsu_factor, 0); 72 | $datareceived = round($jsonData[$i]['rx'] / $dsu_factor, 0); 73 | 74 | if ($timeunits === 'm') { 75 | echo '{ "date": "' , $dt->format('Y-m') , '", "rx": "' , $datareceived , 76 | '", "tx": "' , $datasend , '" }'; 77 | } else { 78 | echo '{ "date": "' , $dt->format('Y-m-d') , '", "rx": "' , $datareceived , 79 | '", "tx": "' , $datasend , '" }'; 80 | } 81 | } 82 | 83 | echo ' ]'; 84 | -------------------------------------------------------------------------------- /ajax/logging/clearlog.php: -------------------------------------------------------------------------------- 1 | $path .'/hostapd.conf', "tmp" => "/tmp/hostapddata", "dest" => RASPI_HOSTAPD_CONFIG), 13 | array("src" => $path .'/dhcpcd.conf', "tmp" => "/tmp/dhcpddata", "dest" => RASPI_DHCPCD_CONFIG), 14 | array("src" => $path .'/090_wlan0.conf', "tmp" => "/tmp/dnsmasqdata", "dest" => RASPI_DNSMASQ_PREFIX.'wlan0.conf'), 15 | array("src" => $path .'/090_raspap.conf', "tmp" => "/tmp/dnsmasqdata", "dest" => RASPI_DNSMASQ_PREFIX.'raspap.conf'), 16 | ); 17 | 18 | foreach ($configs as $config) { 19 | try { 20 | $tmp = file_get_contents($config["src"]); 21 | file_put_contents($config["tmp"], $tmp); 22 | system("sudo cp ".$config["tmp"]. " ".$config["dest"]); 23 | } catch (Exception $e) { 24 | $return = $e->getCode(); 25 | } 26 | } 27 | $jsonData = ['return'=>$return]; 28 | echo json_encode($jsonData); 29 | 30 | -------------------------------------------------------------------------------- /ajax/networking/get_all_interfaces.php: -------------------------------------------------------------------------------- 1 | parseIwInfo($iface); 13 | 14 | echo json_encode($supportedFrequencies); 15 | } 16 | -------------------------------------------------------------------------------- /ajax/networking/get_ip_summary.php: -------------------------------------------------------------------------------- 1 | $intResult,'output'=>$intOutput]; 14 | echo json_encode($jsonData); 15 | } else { 16 | $jsonData = ['return'=>2,'output'=>['Error getting data']]; 17 | echo json_encode($jsonData); 18 | } 19 | -------------------------------------------------------------------------------- /ajax/networking/get_nl80211_band.php: -------------------------------------------------------------------------------- 1 | 0) { 29 | $flags += NL80211_BAND_24GHZ; 30 | } 31 | if (count(preg_grep('/^5[0-9]{3}/i', $frequencies)) >0) { 32 | $flags += NL80211_BAND_5GHZ; 33 | } 34 | 35 | switch ($flags) { 36 | case NL80211_BAND_24GHZ: 37 | $msg = sprintf(_("The selected interface (%s) has support for the 2.4 GHz wireless band only."), $iface); 38 | break; 39 | case NL80211_BAND_5GHZ: 40 | $msg = sprintf(_("The selected interface (%s) has support for the 5 GHz wireless band only."), $iface); 41 | break; 42 | case NL80211_BAND_24GHZ | NL80211_BAND_5GHZ: 43 | $msg = sprintf(_("The selected interface (%s) has support for both the 2.4 and 5 GHz wireless bands."), $iface); 44 | break; 45 | default: 46 | $msg = sprintf(_("The selected interface (%s) does not support wireless mode operation."), $iface); 47 | } 48 | echo json_encode($msg); 49 | } 50 | -------------------------------------------------------------------------------- /ajax/networking/get_wgcfg.php: -------------------------------------------------------------------------------- 1 | $pubkey_tmp", $return); 19 | $wgdata['pubkey'] = str_replace("\n",'',file_get_contents($pubkey_tmp)); 20 | exec("sudo mv $privkey_tmp $privkey", $return); 21 | exec("sudo mv $pubkey_tmp $pubkey", $return); 22 | 23 | echo json_encode($wgdata); 24 | } 25 | -------------------------------------------------------------------------------- /ajax/networking/wifi_stations.php: -------------------------------------------------------------------------------- 1 | $network) $networks[$ssid]["ssidutf8"] = ssid2utf8( $ssid ); 20 | 21 | $connected = array_filter($networks, function($n) { return $n['connected']; } ); 22 | $known = array_filter($networks, function($n) { return !$n['connected'] && $n['configured']; } ); 23 | $nearby = array_filter($networks, function($n) { return !$n['configured']; } ); 24 | 25 | echo renderTemplate('wifi_stations', compact('networks', 'connected', 'known', 'nearby'), true); 26 | -------------------------------------------------------------------------------- /ajax/openvpn/activate_ovpncfg.php: -------------------------------------------------------------------------------- 1 | $return]; 14 | echo json_encode($jsonData); 15 | } 16 | -------------------------------------------------------------------------------- /ajax/plugins/do_plugin_install.php: -------------------------------------------------------------------------------- 1 | installPlugin($plugin_uri, $plugin_version, $install_path); 16 | echo json_encode($return); 17 | } catch (Exception $e) { 18 | http_response_code(422); // unprocessable content 19 | echo json_encode(['error' => $e->getMessage()]); 20 | } 21 | } else { 22 | http_response_code(400); // Bad Request 23 | echo json_encode(['error' => 'Plugin URI, version, and install path are required']); 24 | exit; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /ajax/session/do_check_session.php: -------------------------------------------------------------------------------- 1 | = RASPI_SESSION_TIMEOUT ? 'session_expired' : 'active'; 11 | 12 | if ($status === 'session_expired') { 13 | session_unset(); // unset all session variables 14 | session_destroy(); // destroy the session 15 | } 16 | 17 | // send response 18 | header('Content-Type: application/json'); 19 | header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); 20 | header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); 21 | header('Pragma: no-cache'); 22 | 23 | $response = [ 24 | 'status' => $status, 25 | 'last_activity' => $lastActivity, 26 | 'session_lifetime' => $sessionLifetime 27 | ]; 28 | 29 | echo json_encode($response); 30 | exit(); 31 | 32 | -------------------------------------------------------------------------------- /ajax/system/sys_actions.php: -------------------------------------------------------------------------------- 1 | 1, 11 | 'Updating sources' => 2, 12 | 'Installing required packages' => 3, 13 | 'Cloning latest files' => 4, 14 | 'Installing application' => 5, 15 | 'Installation completed' => 6, 16 | 'error' => 7 17 | ]; 18 | usleep(500); 19 | 20 | if (file_exists($logFile)) { 21 | $handle = fopen($logFile, 'r'); 22 | 23 | if ($handle) { 24 | while (($line = fgets($handle)) !== false) { 25 | foreach ($searchStrings as $searchString => $value) { 26 | if (strpos($line, $searchString) !== false) { 27 | echo $value .PHP_EOL; 28 | flush(); 29 | ob_flush(); 30 | if ($value === 6) { 31 | fclose($handle); 32 | exit(); 33 | } elseif ($value === 7) { 34 | echo $line .PHP_EOL; 35 | fclose($handle); 36 | exit(); 37 | } 38 | } 39 | } 40 | } 41 | fclose($handle); 42 | } else { 43 | echo json_encode("Unable to open file: $logFile"); 44 | } 45 | } else { 46 | echo json_encode("File does not exist: $logFile"); 47 | } 48 | -------------------------------------------------------------------------------- /api/auth.py: -------------------------------------------------------------------------------- 1 | import os 2 | from fastapi.security.api_key import APIKeyHeader 3 | from fastapi import Security, HTTPException 4 | from starlette.status import HTTP_403_FORBIDDEN 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | apikey=os.getenv('RASPAP_API_KEY') 10 | #if env not set, set the api key to "insecure" 11 | if apikey == None: 12 | apikey = "insecure" 13 | 14 | print(apikey) 15 | api_key_header = APIKeyHeader(name="access_token", auto_error=False) 16 | 17 | async def get_api_key(api_key_header: str = Security(api_key_header)): 18 | if api_key_header ==apikey: 19 | return api_key_header 20 | else: 21 | raise HTTPException( 22 | status_code=HTTP_403_FORBIDDEN, detail="403: Unauthorized" 23 | ) 24 | 25 | -------------------------------------------------------------------------------- /api/modules/client.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | 4 | def get_active_clients_amount(interface): 5 | arp_output = subprocess.run(['arp', '-i', interface], capture_output=True, text=True) 6 | mac_addresses = arp_output.stdout.splitlines() 7 | 8 | if mac_addresses: 9 | grep_pattern = '|'.join(mac_addresses) 10 | output = subprocess.run(['grep', '-iwE', grep_pattern, '/var/lib/misc/dnsmasq.leases'], capture_output=True, text=True) 11 | return len(output.stdout.splitlines()) 12 | else: 13 | return 0 14 | 15 | def get_active_clients(interface): 16 | arp_output = subprocess.run(['arp', '-i', interface], capture_output=True, text=True) 17 | arp_mac_addresses = set(line.split()[2] for line in arp_output.stdout.splitlines()[1:]) 18 | 19 | dnsmasq_output = subprocess.run(['cat', '/var/lib/misc/dnsmasq.leases'], capture_output=True, text=True) 20 | active_clients = [] 21 | 22 | for line in dnsmasq_output.stdout.splitlines(): 23 | fields = line.split() 24 | mac_address = fields[1] 25 | 26 | if mac_address in arp_mac_addresses: 27 | client_data = { 28 | "timestamp": int(fields[0]), 29 | "mac_address": fields[1], 30 | "ip_address": fields[2], 31 | "hostname": fields[3], 32 | "client_id": fields[4], 33 | } 34 | active_clients.append(client_data) 35 | 36 | json_output = json.dumps(active_clients, indent=2) 37 | return json_output 38 | 39 | -------------------------------------------------------------------------------- /api/modules/ddns.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | def use(): 4 | return subprocess.run("cat /etc/ddclient.conf | grep use= | cut -d'=' -f2", shell=True, capture_output=True, text=True).stdout.strip() 5 | 6 | def method(): 7 | #get the contents of the line below "use=" 8 | return subprocess.run("awk '/^use=/ {getline; print}' /etc/ddclient.conf | cut -d'=' -f2", shell=True, capture_output=True, text=True).stdout.strip() 9 | 10 | def protocol(): 11 | return subprocess.run("cat /etc/ddclient.conf | grep protocol= | cut -d'=' -f2", shell=True, capture_output=True, text=True).stdout.strip() 12 | 13 | def server(): 14 | return subprocess.run("cat /etc/ddclient.conf | grep server= | cut -d'=' -f2", shell=True, capture_output=True, text=True).stdout.strip() 15 | 16 | def login(): 17 | return subprocess.run("cat /etc/ddclient.conf | grep login= | cut -d'=' -f2", shell=True, capture_output=True, text=True).stdout.strip() 18 | 19 | def password(): 20 | return subprocess.run("cat /etc/ddclient.conf | grep password= | cut -d'=' -f2", shell=True, capture_output=True, text=True).stdout.strip() 21 | 22 | def domain(): 23 | #get the contents of the line below "password=" 24 | return subprocess.run("awk '/^password=/ {getline; print}' /etc/ddclient.conf", shell=True, capture_output=True, text=True).stdout.strip() -------------------------------------------------------------------------------- /api/modules/dhcp.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | 4 | def range_start(): 5 | return subprocess.run("cat /etc/dnsmasq.d/090_wlan0.conf |grep dhcp-range= |cut -d'=' -f2| cut -d',' -f1", shell=True, capture_output=True, text=True).stdout.strip() 6 | 7 | def range_end(): 8 | return subprocess.run("cat /etc/dnsmasq.d/090_wlan0.conf |grep dhcp-range= |cut -d'=' -f2| cut -d',' -f2", shell=True, capture_output=True, text=True).stdout.strip() 9 | 10 | def range_subnet_mask(): 11 | return subprocess.run("cat /etc/dnsmasq.d/090_wlan0.conf |grep dhcp-range= |cut -d'=' -f2| cut -d',' -f3", shell=True, capture_output=True, text=True).stdout.strip() 12 | 13 | def range_lease_time(): 14 | return subprocess.run("cat /etc/dnsmasq.d/090_wlan0.conf |grep dhcp-range= |cut -d'=' -f2| cut -d',' -f4", shell=True, capture_output=True, text=True).stdout.strip() 15 | 16 | def range_gateway(): 17 | return subprocess.run("cat /etc/dhcpcd.conf | grep routers | cut -d'=' -f2", shell=True, capture_output=True, text=True).stdout.strip() 18 | 19 | def range_nameservers(): 20 | output = subprocess.run("cat /etc/dhcpcd.conf", shell=True, capture_output=True, text=True).stdout.strip() 21 | 22 | nameservers = [] 23 | 24 | lines = output.split('\n') 25 | for line in lines: 26 | if "static domain_name_server" in line: 27 | servers = line.split('=')[1].strip().split() 28 | nameservers.extend(servers) 29 | 30 | return nameservers -------------------------------------------------------------------------------- /api/modules/dns.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | 4 | def adblockdomains(): 5 | output = subprocess.run("cat /etc/raspap/adblock/domains.txt", shell=True, capture_output=True, text=True).stdout.strip() 6 | domains =output.split('\n') 7 | domainlist=[] 8 | for domain in domains: 9 | if domain.startswith('#') or domain=="": 10 | continue 11 | domainlist.append(domain.split('=/')[1]) 12 | return domainlist 13 | 14 | def adblockhostnames(): 15 | output = subprocess.run("cat /etc/raspap/adblock/hostnames.txt", shell=True, capture_output=True, text=True).stdout.strip() 16 | hostnames = output.split('\n') 17 | hostnamelist=[] 18 | for hostname in hostnames: 19 | if hostname.startswith('#') or hostname=="": 20 | continue 21 | hostnamelist.append(hostname.replace('0.0.0.0 ','')) 22 | return hostnamelist 23 | 24 | def upstream_nameserver(): 25 | return subprocess.run("awk '/nameserver/ {print $2}' /run/dnsmasq/resolv.conf", shell=True, capture_output=True, text=True).stdout.strip() 26 | 27 | def dnsmasq_logs(): 28 | output = subprocess.run("cat /var/log/dnsmasq.log", shell=True, capture_output=True, text=True).stdout.strip() 29 | log_entries = [] 30 | for line in output.split("\n"): 31 | fields = line.split(" ") 32 | log_dict = { 33 | 'timestamp': ' '.join(fields[:3]), 34 | 'process': fields[3][:-1], # Remove the trailing colon 35 | 'message': ' '.join(fields[4:]), 36 | } 37 | log_entries.append(log_dict) 38 | return log_entries -------------------------------------------------------------------------------- /api/modules/firewall.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | def firewall_rules(): 4 | return subprocess.run("cat /etc/raspap/networking/firewall/iptables_rules.json", shell=True, capture_output=True, text=True).stdout.strip() -------------------------------------------------------------------------------- /api/modules/networking.py: -------------------------------------------------------------------------------- 1 | import psutil 2 | import json 3 | 4 | def throughput(): 5 | interface_info = {} 6 | 7 | # Get network interfaces 8 | interfaces = psutil.net_if_stats() 9 | 10 | for interface, stats in interfaces.items(): 11 | if interface.startswith("lo") or interface.startswith("docker"): 12 | # Skip loopback and docker interface 13 | continue 14 | 15 | try: 16 | # Get network traffic statistics 17 | traffic_stats = psutil.net_io_counters(pernic=True)[interface] 18 | rx_packets = traffic_stats[1] 19 | rx_bytes = traffic_stats[0] 20 | tx_packets = traffic_stats[3] 21 | tx_bytes = traffic_stats[4] 22 | 23 | interface_info[interface] = { 24 | "RX_packets": rx_packets, 25 | "RX_bytes": rx_bytes, 26 | "TX_packets": tx_packets, 27 | "TX_bytes": tx_bytes 28 | } 29 | except KeyError: 30 | # Handle the case where network interface statistics are not available 31 | pass 32 | 33 | return json.dumps(interface_info, indent=2) 34 | 35 | def interfaces(): 36 | interface_info = {} 37 | 38 | # Get network interfaces 39 | interfaces = psutil.net_if_addrs() 40 | 41 | for interface, addrs in interfaces.items(): 42 | if interface.startswith("lo") or interface.startswith("docker"): 43 | # Skip loopback and docker interface 44 | continue 45 | 46 | ip_address = None 47 | netmask = None 48 | mac_address = None 49 | 50 | for addr in addrs: 51 | if addr.family == 2: # AF_INET corresponds to the integer value 2 52 | # IPv4 address 53 | ip_address = addr.address 54 | netmask = addr.netmask 55 | 56 | # Get MAC address 57 | for addr in psutil.net_if_addrs().get(interface, []): 58 | if addr.family == psutil.AF_LINK: 59 | mac_address = addr.address 60 | 61 | interface_info[interface] = { 62 | "IP_address": ip_address, 63 | "Netmask": netmask, 64 | "MAC_address": mac_address 65 | } 66 | return json.dumps(interface_info, indent=2) 67 | 68 | #TODO: migrate to vnstat, to lose psutil dependency -------------------------------------------------------------------------------- /api/modules/openvpn.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | def client_configs(): 4 | return subprocess.run("find /etc/openvpn/client/ -type f | wc -l", shell=True, capture_output=True, text=True).stdout.strip() 5 | 6 | def client_config_names(): 7 | config_names_list = [] 8 | output = subprocess.run('''ls /etc/openvpn/client/ | grep -v "^client.conf$"''', shell=True, capture_output=True, text=True).stdout.strip() 9 | lines = output.split("\n") 10 | for client in lines: 11 | if "_client" in client: 12 | config_names_dict ={'config':client} 13 | config_names_list.append(config_names_dict) 14 | return config_names_list 15 | 16 | def client_login_names(): 17 | config_names_list = [] 18 | output = subprocess.run('''ls /etc/openvpn/client/ | grep -v "^client.conf$"''', shell=True, capture_output=True, text=True).stdout.strip() 19 | lines = output.split("\n") 20 | for client in lines: 21 | if "_login" in client: 22 | config_names_dict ={'login':client} 23 | config_names_list.append(config_names_dict) 24 | return config_names_list 25 | 26 | def client_config_active(): 27 | output = subprocess.run('''ls -al /etc/openvpn/client/ | grep "client.conf -"''', shell=True, capture_output=True, text=True).stdout.strip() 28 | active_config = output.split("/etc/openvpn/client/") 29 | return(active_config[1]) 30 | 31 | def client_login_active(): 32 | output = subprocess.run('''ls -al /etc/openvpn/client/ | grep "login.conf -"''', shell=True, capture_output=True, text=True).stdout.strip() 33 | active_config = output.split("/etc/openvpn/client/") 34 | return(active_config[1]) 35 | 36 | def client_config_list(client_config): 37 | output = subprocess.run(["cat", f"/etc/openvpn/client/{client_config}"], capture_output=True, text=True).stdout.strip() 38 | return output.split('\n') 39 | 40 | #TODO: where is the logfile?? 41 | #TODO: is service connected? 42 | -------------------------------------------------------------------------------- /api/modules/wireguard.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import re 3 | 4 | def configs(): 5 | #ignore symlinks, because wg0.conf is in production the main config, but in insiders it is a symlink 6 | return subprocess.run("find /etc/wireguard/ -type f | wc -l", shell=True, capture_output=True, text=True).stdout.strip() 7 | 8 | def client_config_names(): 9 | config_names_list = [] 10 | output = subprocess.run('''ls /etc/wireguard/ | grep -v "^wg0.conf$"''', shell=True, capture_output=True, text=True).stdout.strip() 11 | lines = output.split("\n") 12 | for client in lines: 13 | config_names_dict ={'config':client} 14 | config_names_list.append(config_names_dict) 15 | return config_names_list 16 | 17 | def client_config_active(): 18 | output = subprocess.run('''ls -al /etc/wireguard/ | grep "wg0.conf -"''', shell=True, capture_output=True, text=True).stdout.strip() 19 | active_config = output.split("/etc/wireguard/") 20 | return(active_config[1]) 21 | 22 | #TODO: where is the logfile?? 23 | #TODO: is service connected? 24 | -------------------------------------------------------------------------------- /api/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi==0.109.1 2 | uvicorn==0.25.0 3 | psutil==5.9.8 4 | python-dotenv==1.0.1 5 | 6 | -------------------------------------------------------------------------------- /app/css/dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme Name: Lights Out 3 | Author: @billz 4 | Author URI: https://github.com/billz 5 | Description: A Bootstrap dark mode theme for RaspAP 6 | License: GNU General Public License v3.0 7 | */ 8 | 9 | @import url('custom.php'); 10 | 11 | html[data-bs-theme="dark"] { 12 | background-color: var(--bs-dark); 13 | color: var(--bs-light); 14 | } 15 | 16 | html[data-bs-theme="dark"] body, 17 | html[data-bs-theme="dark"] footer, 18 | html[data-bs-theme="dark"] .sb-sidenav, 19 | html[data-bs-theme="dark"] .sb-topnav, 20 | html[data-bs-theme="dark"] .card, 21 | html[data-bs-theme="dark"] .card-footer { 22 | background-color: var(--bs-dark); 23 | } 24 | 25 | html[data-bs-theme="dark"] .card-body, 26 | html[data-bs-theme="dark"] .card-footer, 27 | html[data-bs-theme="dark"] .info-item-xs, 28 | html[data-bs-theme="dark"] .table>:not(caption)>*>* { 29 | color: var(--bs-secondary); 30 | } 31 | 32 | html[data-bs-theme="dark"] .sb-topnav.navbar { 33 | background-color: var(--bs-dark) !important; 34 | color: var(--bs-light); 35 | } 36 | 37 | html[data-bs-theme="dark"] .sb-topnav.navbar, 38 | html[data-bs-theme="dark"] .sb-topnav.navbar a { 39 | color: var(--raspap-theme-color) !important; 40 | } 41 | 42 | html[data-bs-theme="dark"] .sb-topnav.navbar a:hover, 43 | html[data-bs-theme="dark"] .sb-topnav.navbar a:focus { 44 | color: var(--raspap-theme-darker) !important; 45 | } 46 | 47 | html[data-bs-theme="dark"] .sb-topnav.navbar { 48 | --bs-navbar-bg: var(--bs-dark); 49 | } 50 | 51 | html[data-bs-theme="dark"] .sb-topnav.navbar.navbar-light { 52 | color: var(--bs-light); 53 | } 54 | 55 | html[data-bs-theme="dark"] .card { 56 | border-color: var(--bs-secondary); 57 | color: var(--bs-light); 58 | } 59 | 60 | html[data-bs-theme="dark"] .card-footer { 61 | border-color: var(--bs-secondary); 62 | } 63 | 64 | html[data-bs-theme="dark"] .nav-tabs { 65 | --bs-nav-tabs-link-active-color: var(--bs-light); 66 | --bs-nav-tabs-link-active-bg: var(--bs-dark); 67 | --bs-nav-tabs-link-active-border-color: var(--bs-secondary) var(--bs-secondary) var(--bs-dark); 68 | --bs-nav-tabs-border-color: var(--bs-secondary); 69 | --bs-nav-tabs-link-hover-border-color: var(--bs-secondary); 70 | } 71 | 72 | html[data-bs-theme="dark"] .btn { 73 | color: var(--bs-gray-800); 74 | opacity: 75%; 75 | } 76 | 77 | html[data-bs-theme="dark"] select, 78 | html[data-bs-theme="dark"] .form-control { 79 | background-color: var(--bs-dark); 80 | border-color: var(--bs-secondary); 81 | color: var(--bs-light); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /app/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaspAP/raspap-webgui/1b39a7a1bf8a3c02bbb106a7ce2861f9858428ab/app/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /app/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaspAP/raspap-webgui/1b39a7a1bf8a3c02bbb106a7ce2861f9858428ab/app/icons/favicon-96x96.png -------------------------------------------------------------------------------- /app/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaspAP/raspap-webgui/1b39a7a1bf8a3c02bbb106a7ce2861f9858428ab/app/icons/favicon.ico -------------------------------------------------------------------------------- /app/icons/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RaspAP Admin Panel", 3 | "short_name": "RaspAP", 4 | "icons": [ 5 | { 6 | "src": "/app/icons/web-app-manifest-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png", 9 | "purpose": "maskable" 10 | }, 11 | { 12 | "src": "/app/icons/web-app-manifest-512x512.png", 13 | "sizes": "512x512", 14 | "type": "image/png", 15 | "purpose": "maskable" 16 | } 17 | ], 18 | "theme_color": "#ffffff", 19 | "background_color": "#ffffff", 20 | "display": "standalone" 21 | } 22 | -------------------------------------------------------------------------------- /app/icons/web-app-manifest-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaspAP/raspap-webgui/1b39a7a1bf8a3c02bbb106a7ce2861f9858428ab/app/icons/web-app-manifest-192x192.png -------------------------------------------------------------------------------- /app/icons/web-app-manifest-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaspAP/raspap-webgui/1b39a7a1bf8a3c02bbb106a7ce2861f9858428ab/app/icons/web-app-manifest-512x512.png -------------------------------------------------------------------------------- /app/img/dashed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/img/insiders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaspAP/raspap-webgui/1b39a7a1bf8a3c02bbb106a7ce2861f9858428ab/app/img/insiders.png -------------------------------------------------------------------------------- /app/img/raspAP-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaspAP/raspap-webgui/1b39a7a1bf8a3c02bbb106a7ce2861f9858428ab/app/img/raspAP-logo.png -------------------------------------------------------------------------------- /app/img/right-dashed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/img/right-solid.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 23 | 24 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/img/solid.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 0.75, 20 | 'out' => 297.75, 21 | 'device-2' => 198.75, 22 | 'device-3' => 397.058, 23 | 'device-4' => 595.211 24 | ]; 25 | 26 | // Calculate joint line segments 27 | if ($showJoint) { 28 | $activeDevices = array_filter([$showDevice1, $showDevice2, $showDevice3, $showDevice4]); 29 | $activeYs = []; 30 | 31 | foreach ($devicePositions as $device => $y) { 32 | if (isset($_GET[$device])) { 33 | $activeYs[] = $y; 34 | } 35 | } 36 | 37 | // Add top/bottom if first/last device is connected 38 | if ($showDevice1) array_unshift($activeYs, 0); 39 | if ($showDevice4) $activeYs[] = 596; 40 | 41 | // Draw segments between consecutive points 42 | for ($i = 1; $i < count($activeYs); $i++) { 43 | $y1 = $activeYs[$i-1]; 44 | $y2 = $activeYs[$i]; 45 | echo ""; 46 | } 47 | } 48 | ?> 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/img/uri-qr-code.php: -------------------------------------------------------------------------------- 1 | daterxtx')}("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); -------------------------------------------------------------------------------- /app/js/huebee.js: -------------------------------------------------------------------------------- 1 | // Initialize Huebee color picker 2 | var elem = document.querySelector('.color-input'); 3 | var hueb = new Huebee( elem, { 4 | notation: 'hex', 5 | saturations: 2, 6 | customColors: [ '#d8224c', '#dd4814', '#ea0', '#19f', '#333' ], 7 | className: 'light-picker', 8 | hue0: 210 9 | }); 10 | 11 | // Set custom color if defined 12 | var color = getCookie('color'); 13 | if (color == null || color == '') { 14 | color = '#2b8080'; 15 | } 16 | hueb.setColor(color); 17 | 18 | // Change event 19 | hueb.on( 'change', function( color, hue, sat, lum ) { 20 | setCookie('color',color,90); 21 | }) 22 | 23 | -------------------------------------------------------------------------------- /app/js/linkquality.js: -------------------------------------------------------------------------------- 1 | // Link quality gauge for ChartJS 2 | 3 | // Support for dark theme 4 | theme = getCookie('theme'); 5 | if (theme == 'lightsout.css') { 6 | var borderColor = 'rgba(37, 153, 63, 1)'; 7 | var labelColor = 'rgba(37, 153, 63, 1)'; 8 | } else if (theme == 'material-light.php') { 9 | var borderColor = '#f2f2fb'; 10 | var labelColor = '#f2f2fb'; 11 | } else if (theme == 'material-dark.php') { 12 | var borderColor = '#f2f2fb'; 13 | var labelColor = '#f2f2fb'; 14 | } else { 15 | var borderColor = 'rgba(147, 210, 162, 1)'; 16 | var labelColor = 'rgba(130, 130, 130, 1)'; 17 | } 18 | 19 | let data1 = { 20 | datasets: [{ 21 | data: [linkQ, 100-linkQ], 22 | backgroundColor: 'transparent', 23 | borderColor: borderColor, 24 | }], 25 | }; 26 | 27 | let config = { 28 | type: 'doughnut', 29 | data: data1, 30 | options: { 31 | aspectRatio: 2, 32 | responsive: true, 33 | maintainAspectRatio: false, 34 | tooltips: {enabled: false}, 35 | hover: {mode: null}, 36 | legend: { 37 | display: false, 38 | }, 39 | rotation: (2/3)*Math.PI,//2+(1/3), 40 | circumference: (1+(2/3)) * Math.PI, // * Math.PI, 41 | cutoutPercentage: 80, 42 | animation: { 43 | animateScale: false, 44 | animateRotate: true 45 | }, 46 | tooltips: { 47 | enabled: false 48 | } 49 | }, 50 | centerText: { 51 | display: true, 52 | text: linkQ + "%" 53 | }, 54 | plugins: [{ 55 | beforeDraw: function(chart) { 56 | if (chart.config.centerText.display !== null && 57 | typeof chart.config.centerText.display !== 'undefined' && 58 | chart.config.centerText.display) { 59 | drawLinkQ(chart); 60 | } 61 | } 62 | }] 63 | }; 64 | 65 | function drawLinkQ(chart) { 66 | 67 | let width = chart.chart.width; 68 | let height = chart.chart.height; 69 | let ctx = chart.chart.ctx; 70 | 71 | ctx.restore(); 72 | let fontSize = (height / 100).toFixed(2); 73 | ctx.font = fontSize + "em sans-serif"; 74 | ctx.fillStyle = labelColor; 75 | ctx.textBaseline = "middle"; 76 | 77 | let text = chart.config.centerText.text; 78 | let textX = Math.round((width - ctx.measureText(text).width) * 0.5); 79 | let textY = height / 2; 80 | ctx.fillText(text, textX, textY); 81 | ctx.save(); 82 | } 83 | 84 | window.onload = function() { 85 | let ctx = document.getElementById("divChartLinkQ").getContext("2d"); 86 | var chart = new Chart(ctx, config); 87 | }; 88 | 89 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raspap/raspap-webgui", 3 | "description": "Simple wireless AP setup and mangement for Debian-based devices", 4 | "license": "GPL-3.0", 5 | "homepage": "https://raspap.com/", 6 | "keywords": ["raspberrypi", "debian", "armbian", "wifi"], 7 | "type": "raspap-core", 8 | "authors": [ 9 | { 10 | "name": "RaspAP Team", 11 | "email": "billzimmerman@gmail.com", 12 | "homepage": "https://raspap.com/" 13 | } 14 | ], 15 | "require": { 16 | "php": "^8.2", 17 | "phpoption/phpoption": "^1.9", 18 | "ext-mbstring": "*" 19 | }, 20 | "require-dev": { 21 | "php-parallel-lint/php-parallel-lint": "^1.2.0", 22 | "phpcompatibility/php-compatibility": "^9.3.5", 23 | "squizlabs/php_codesniffer": "^3.9.0", 24 | "ext-simplexml": "*" 25 | }, 26 | "scripts": { 27 | "lint": "parallel-lint . --exclude vendor", 28 | "phpcs": "phpcs -p -s --config-set installed_paths vendor/phpcompatibility/php-compatibility .", 29 | "test": [ 30 | "composer lint", 31 | "composer phpcs" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config/090_raspap.conf: -------------------------------------------------------------------------------- 1 | # RaspAP default config 2 | log-facility=/var/log/dnsmasq.log 3 | conf-dir=/etc/dnsmasq.d 4 | 5 | -------------------------------------------------------------------------------- /config/090_wlan0.conf: -------------------------------------------------------------------------------- 1 | # RaspAP wlan0 configuration for wired (ethernet) AP mode 2 | interface=wlan0 3 | domain-needed 4 | dhcp-range=10.3.141.50,10.3.141.254,255.255.255.0,12h 5 | dhcp-option=6,9.9.9.9,1.1.1.1 6 | 7 | -------------------------------------------------------------------------------- /config/50-raspap-router.conf: -------------------------------------------------------------------------------- 1 | server.modules += ( 2 | "mod_rewrite", 3 | ) 4 | 5 | $HTTP["url"] =~ "^/REPLACE_ME/(?!(dist|app|ajax|config)).*" { 6 | url.rewrite-once = ( "^/REPLACE_ME/(.*?)(\?.+)?$"=>"/REPLACE_ME/index.php/$1$2" ) 7 | server.error-handler-404 = "/REPLACE_ME/index.php" 8 | } 9 | 10 | -------------------------------------------------------------------------------- /config/blocklists.json: -------------------------------------------------------------------------------- 1 | { 2 | "StevenBlack/hosts": [ 3 | "StevenBlack/hosts (default)" 4 | ], 5 | "badmojr/hosts": [ 6 | "badmojr/1Hosts (Mini)", 7 | "badmojr/1Hosts (Lite)", 8 | "badmojr/1Hosts (Pro)", 9 | "badmojr/1Hosts (Xtra)" 10 | ], 11 | "OISD/domains": [ 12 | "oisd/big (default)", 13 | "oisd/small", 14 | "oisd/nsfw" 15 | ] 16 | } 17 | 18 | -------------------------------------------------------------------------------- /config/client_config/70-mobile-data-sticks.rules: -------------------------------------------------------------------------------- 1 | # mobile data modem - ttyUSB0 device appears 2 | SUBSYSTEM=="tty", KERNEL=="ttyUSB0", TAG+="systemd", ENV{SYSTEMD_WANTS}="start start_ppp0_device.service" 3 | 4 | 5 | -------------------------------------------------------------------------------- /config/client_config/80-raspap-net-devices.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="net", ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14db", NAME="hilink%n", TAG+="systemd", ENV{SYSTEMD_WANTS}="start start_huawei_hilink@hilink%n.service" 2 | 3 | 4 | -------------------------------------------------------------------------------- /config/client_config/info_huawei_modem.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Information about HUAWEI modem - via AT commands 3 | # ------------------------------------------------ 4 | # get info about the device and signal 5 | # parameter: $1 - see opts list below 6 | # $2 - tty device name for the communicaton (optional) 7 | # returns the value of the parameter, or "none" if not found or empty 8 | # 9 | # requires: socat 10 | # 11 | # zbchristian 2020 12 | 13 | opts=("manufacturer" "device" "imei" "imsi" "telnumber" "mode" "signal" "operator") 14 | 15 | # at command to extract information 16 | atcmds=("AT+CGMI" "AT+CGMM" "AT+CGSN" "AT+CIMI" "AT+CNUM" "AT+COPS?" "AT+CSQ" "AT+COPS?") 17 | # regexp pattern to extract wanted information from result string 18 | pats=( " " " " " " " " ".*\,\"([0-9\+]*)\".*" '.*\,([0-9])$' ".*: ([0-9]*).*" '.*\,\"([^ ]*)\".*$') 19 | 20 | # tty device for communication - usually 3 tty devices are created and the 3rd ttyUSB2 is available, even, when the device is connected 21 | dev="/dev/ttyUSB2" 22 | 23 | atsilent="AT^CURC=0" 24 | 25 | if [ ! -z $2 ]; then dev=$2; fi 26 | 27 | idx=-1 28 | opt=${opts[0]} 29 | if [ ! -z $1 ]; then opt=$1; fi 30 | 31 | for i in "${!opts[@]}"; do 32 | if [[ ${opts[$i]} == $opt ]]; then idx=$i; fi 33 | done 34 | if [[ $idx == -1 ]];then echo "none"; exit; fi 35 | 36 | atcmd=${atcmds[$idx]} 37 | pat=${pats[$idx]} 38 | 39 | 40 | result=`(echo $atsilent; echo $atcmd) | sudo /usr/bin/socat - $dev` 41 | # escape the AT command to be used in the regexp 42 | atesc=${atcmd//[\+]/\\+} 43 | atesc=${atesc//[\?]/\\?} 44 | result=`echo $result | sed -rn 's/.*'"$atesc"'\s([^ ]+|[^ ]+ [^ ]+)\sOK.*$/\1/pg'` 45 | if [[ $pat != " " ]]; then 46 | result=`echo $result | sed -rn 's/'"$pat"'/\1/pg'` 47 | fi 48 | 49 | if [ -z "$result" ]; then result="none"; fi 50 | 51 | echo $result 52 | 53 | -------------------------------------------------------------------------------- /config/client_config/interfaces: -------------------------------------------------------------------------------- 1 | # interfaces(5) file used by ifup(8) and ifdown(8) 2 | 3 | # Please note that this file is written to be used with dhcpcd 4 | # For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf' 5 | 6 | # Include files from /etc/network/interfaces.d: 7 | source-directory /etc/network/interfaces.d 8 | 9 | auto ppp0 10 | iface ppp0 inet wvdial 11 | provider connect 12 | pre-up /usr/local/sbin/ppp0_setpin.sh 13 | up /usr/local/sbin/ppp0_route.sh 14 | -------------------------------------------------------------------------------- /config/client_config/onoff_huawei_hilink.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # connect/disconnect Huawei mobile data stick in Hilink mode (e.g. E3372h) 3 | # ======================================================================== 4 | # 5 | # options: -u, --user - user name (default "admin") 6 | # -P, --password - password 7 | # -h, --host - host ip address (default 192.168.8.1) 8 | # -d, --devname - device name (IP is extracted using default route) 9 | # -p, --pin - PIN of SIM card 10 | # -c, --connect - connect 0/1 to set datamode off/on 11 | # 12 | # required software: curl, base64, sha256sum 13 | # 14 | # zbchristian 2021 15 | 16 | # include the hilink API (defaults: hilink_user=admin, hilink_host=192.168.8.1) 17 | source /usr/local/sbin/huawei_hilink_api.sh 18 | 19 | # include the raspap helper functions 20 | source /usr/local/sbin/raspap_helpers.sh 21 | 22 | datamode="" 23 | devname="" 24 | while [ -n "$1" ]; do 25 | case "$1" in 26 | -u|--user) hilink_user="$2"; shift ;; 27 | -P|--password) hilink_password="$2"; shift ;; 28 | -p|--pin) if [[ $2 =~ ^[0-9]{4,8} ]]; then hilink_pin="$2"; fi; shift ;; 29 | -h|--host) hilink_host="$2"; shift ;; 30 | -d|--devname) devname="$2"; shift ;; 31 | -c|--connect) if [ "$2" = "1" ]; then datamode=1; else datamode=0; fi; shift ;; 32 | esac 33 | shift 34 | done 35 | 36 | if [ ! -z "$devname" ]; then # get host IP for given device name 37 | gw=$(ip route list | sed -rn "s/default via (([0-9]{1,3}\.){3}[0-9]{1,3}).*dev $devname.*/\1/p") 38 | if [ -z "$gw" ]; then exit; fi # device name not found in routing list -> abort 39 | hilink_host="$gw" 40 | fi 41 | 42 | if [ -z "$hilink_password" ] || [ -z "$hilink_pin" ]; then 43 | _getAuthRouter 44 | if [ ! -z "$raspap_user" ]; then hilink_user="$raspap_user"; fi 45 | if [ ! -z "$raspap_password" ]; then hilink_password="$raspap_password"; fi 46 | if [ ! -z "$raspap_pin" ]; then hilink_pin="$raspap_pin"; fi 47 | fi 48 | 49 | echo "Hilink: switch device at $hilink_host to mode $datamode" | systemd-cat 50 | 51 | status="usage: -c 1/0 to disconnect/connect" 52 | if [ -z "$datamode" ] || [ ! _initHilinkAPI ]; then echo "Hilink: failed - return status: $status"; exit; fi 53 | 54 | if ! _switchMobileData "$datamode"; then echo -n "Hilink: could not switch the data mode on/off . Error: ";_getErrorText; fi 55 | 56 | if ! _closeHilinkAPI; then echo -n "Hilink: failed - return status: $status . Error: ";_getErrorText; fi 57 | 58 | 59 | -------------------------------------------------------------------------------- /config/client_config/ppp0_route.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # get gateway and ip address of UTMS modem connected to ppp0 4 | # add a default route 5 | # called by /etc/network/interfaces.d/ppp0, when device is coming up 6 | # 7 | ppp0rt="" 8 | let i=1 9 | while [ -z "$ppp0rt" ] ; do 10 | let i+=1 11 | if [ $i -gt 20 ]; then 12 | exit 1 13 | fi 14 | sleep 1 15 | ppp0rt=`ip route list | grep -m 1 ppp0` 16 | done 17 | gate=`echo $ppp0rt | sed -rn 's/(([0-9]{1,3}\.){3}[0-9]{1,3}).*ppp0.*src (([0-9]{1,3}\.){3}[0-9]{1,3})/\1/p'` 18 | src=`echo $ppp0rt | sed -rn 's/(([0-9]{1,3}\.){3}[0-9]{1,3}).*ppp0.*src (([0-9]{1,3}\.){3}[0-9]{1,3})/\3/p'` 19 | 20 | ip route add default via $gate proto dhcp src $src metric 10 21 | exit 0 22 | -------------------------------------------------------------------------------- /config/client_config/ppp0_setpin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # in case /dev/ttyUSB0 does not exist, wait for it at most 30 seconds 3 | let i=1 4 | while ! test -c /dev/ttyUSB0; do 5 | let i+=1 6 | if [ $i -gt 2 ]; then 7 | logger -s -t setpin "/dev/ttyUSB0 does not exist" 8 | exit 3 9 | fi 10 | logger -s -t setpin "waiting 3 seconds for /dev/ttyUSB0" 11 | sleep 3 12 | done 13 | # check for pin and set it if necessary 14 | wvdial pinstatus 2>&1 | grep -q '^+CPIN: READY' 15 | if [ $? -eq 0 ]; then 16 | logger -s -t setpin "SIM card is ready to use :-)" 17 | else 18 | logger -s -t setpin "setting PIN" 19 | wvdial pin 2>/dev/null 20 | fi 21 | exit 0 22 | -------------------------------------------------------------------------------- /config/client_config/raspap_helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Helper functions to extract informations from RaspAP config/settings 4 | # 5 | # zbchristian 2021 6 | # 7 | # get the values of a RaspAP config variable 8 | # call: _getRaspapConfig RASPAP_MOBILEDATA_CONFIG 9 | 10 | raspap_webroot="/var/www/html" 11 | 12 | function _getWebRoot() { 13 | local path 14 | path=$(cat /etc/lighttpd/lighttpd.conf | sed -rn "s/server.document-root \s*= \"([^ \s]*)\"/\1/p") 15 | if [ ! -z "$path" ]; then raspap_webroot="$path"; fi 16 | if [ -z "$path" ]; then return 1; else return 0; fi 17 | } 18 | 19 | # expand an RaspAP config variable utilizing PHP 20 | function _getRaspapConfig() { 21 | local conf var 22 | raspap_config="" 23 | var="$1" 24 | if [ ! -z "$var" ]; then 25 | if ! _getWebRoot; then return 1; fi 26 | conf="$raspap_webroot/includes/config.php" 27 | if [ -f "$conf" ]; then 28 | conf=$(php -r 'include "'$conf'"; echo '$var';' 2> /dev/null) 29 | if [ ! -z "$conf" ] && [ -d ${conf%/*} ]; then raspap_config="$conf"; fi 30 | fi 31 | fi 32 | if [ -z "$raspap_config" ]; then return 1; else return 0; fi 33 | } 34 | 35 | # Username and password for mobile data devices is stored in a file (RASPAP_MOBILEDATA_CONFIG) 36 | function _getAuthRouter() { 37 | local mfile mdata pin user pw 38 | if ! _getRaspapConfig "RASPI_MOBILEDATA_CONFIG"; then return 1; fi 39 | mfile="$raspap_config" 40 | if [ -f $mfile ]; then 41 | mdata=$(cat "$mfile") 42 | pin=$(echo "$mdata" | sed -rn 's/pin = ([^ \s]*)/\1/ip') 43 | if [ ! -z "$pin" ]; then raspap_pin="$pin"; fi 44 | user=$(echo "$mdata" | sed -rn 's/router_user = ([^ \s]*)/\1/ip') 45 | if [ ! -z "$user" ]; then raspap_user="$user"; fi 46 | pw=$(echo "$mdata" | sed -rn 's/router_pw = ([^ \s]*)/\1/ip') 47 | if [ ! -z "$pw" ]; then raspap_password="$pw"; fi 48 | return 0 49 | fi 50 | return 1 51 | } 52 | -------------------------------------------------------------------------------- /config/client_config/start_huawei_hilink@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Bring up HUAWEI mobile hilink device 3 | 4 | [Service] 5 | Type=oneshot 6 | RemainAfterExit=no 7 | ExecStart=/bin/sleep 15 8 | ExecStart=/usr/local/sbin/onoff_huawei_hilink.sh -c 1 -d %i 9 | 10 | [Install] 11 | Alias=start_ltemodem.service 12 | WantedBy=multi-user.target 13 | 14 | -------------------------------------------------------------------------------- /config/client_config/start_ppp0_device.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start ppp0 interface 3 | BindsTo=dev-ttyUSB0.device 4 | After=dev-ttyUSB0.device 5 | 6 | [Service] 7 | Type=forking 8 | RemainAfterExit=yes 9 | ExecStart=/sbin/ifup ppp0 10 | ExecStop=/sbin/ifdown ppp0 11 | 12 | [Install] 13 | Alias=startppp0.service 14 | WantedBy=multi-user.target 15 | 16 | 17 | -------------------------------------------------------------------------------- /config/client_config/wvdial.conf: -------------------------------------------------------------------------------- 1 | [Dialer Defaults] 2 | Modem Type = Analog Modem 3 | ISDN = 0 4 | Baud = 9600 5 | Modem = /dev/ttyUSB0 6 | 7 | [Dialer pin] 8 | Init1 = AT+CPIN="XXXX" 9 | 10 | [Dialer connect] 11 | Init1 = ATZ 12 | Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0 13 | Init3 = AT+CGDCONT=1,"IP","web.vodafone.de" 14 | New PPPD = yes 15 | Phone = *99# 16 | Password = me 17 | Username = vodafone 18 | Stupid Mode = 1 19 | 20 | [Dialer pinstatus] 21 | Init1 = AT+CPIN? 22 | -------------------------------------------------------------------------------- /config/defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "dhcp": { 3 | "wlan0": { 4 | "static ip_address": [ "10.3.141.1/24" ], 5 | "static routers": [ "10.3.141.1" ], 6 | "static domain_name_server": [ "1.1.1.1 8.8.8.8" ], 7 | "subnetmask": [ "255.255.255.0" ] 8 | }, 9 | "wlan1": { 10 | "static ip_address": [ "10.9.141.1/24" ], 11 | "static routers": [ "10.9.141.1" ], 12 | "static domain_name_server": [ "1.1.1.1 8.8.8.8" ], 13 | "subnetmask": [ "255.255.255.0" ] 14 | }, 15 | "uap0": { 16 | "static ip_address": [ "192.168.50.1/24" ], 17 | "static routers": [ "192.168.50.1" ], 18 | "static domain_name_server": [ "1.1.1.1 8.8.8.8" ], 19 | "subnetmask": [ "255.255.255.0" ] 20 | }, 21 | "options": { 22 | "# RaspAP default configuration": null, 23 | "hostname": null, 24 | "clientid": null, 25 | "persistent": null, 26 | "option rapid_commit": null, 27 | "option domain_name_servers, domain_name, domain_search, host_name": null, 28 | "option classless_static_routes": null, 29 | "option ntp_servers": null, 30 | "require dhcp_server_identifier": null, 31 | "slaac private": null, 32 | "nohook lookup-hostname": null 33 | } 34 | }, 35 | "dnsmasq": { 36 | "wlan0": { 37 | "dhcp-range": [ "10.3.141.50,10.3.141.254,255.255.255.0,12h" ] 38 | }, 39 | "wlan1": { 40 | "dhcp-range": [ "10.9.141.50,10.9.141.254,255.255.255.0,12h" ] 41 | }, 42 | "uap0": { 43 | "dhcp-range": [ "192.168.50.50,192.168.50.150,12h" ] 44 | } 45 | }, 46 | "wireguard": { 47 | "server": { 48 | "Address": [ "10.8.2.1/24" ], 49 | "ListenPort": [ "51820" ], 50 | "DNS": [ "9.9.9.9" ], 51 | "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" ], 52 | "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" ] 53 | }, 54 | "peer": { 55 | "Address": [ "10.8.1.2/24" ], 56 | "Endpoint": [ "10.8.2.1:51820" ], 57 | "ListenPort": [ "21841" ], 58 | "AllowedIPs": ["10.8.2.0/24"], 59 | "PersistentKeepalive": [ "15" ] 60 | } 61 | }, 62 | "txpower": { 63 | "dbm": [ "auto", "30", "20", "17", "10", "6", "3", "1", "0" ] 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /config/dhcpcd.conf: -------------------------------------------------------------------------------- 1 | # RaspAP default configuration 2 | hostname 3 | clientid 4 | persistent 5 | option rapid_commit 6 | option domain_name_servers, domain_name, domain_search, host_name 7 | option classless_static_routes 8 | option ntp_servers 9 | require dhcp_server_identifier 10 | slaac private 11 | nohook lookup-hostname 12 | 13 | # RaspAP wlan0 configuration 14 | interface wlan0 15 | static ip_address=10.3.141.1/24 16 | static routers=10.3.141.1 17 | static domain_name_server=9.9.9.9 1.1.1.1 18 | nogateway 19 | 20 | -------------------------------------------------------------------------------- /config/dns-servers.json: -------------------------------------------------------------------------------- 1 | { 2 | "Cloudflare": [ 3 | "1.0.0.1", 4 | "1.1.1.1" 5 | ], 6 | "Freenom World": [ 7 | "80.80.80.80", 8 | "80.80.81.81" 9 | ], 10 | "German Privacy Foundation": [ 11 | "62.141.58.13", 12 | "85.25.251.254", 13 | "87.118.100.175", 14 | "94.75.228.29" 15 | ], 16 | "Google": [ 17 | "8.8.4.4", 18 | "8.8.8.8" 19 | ], 20 | "OpenDNS": [ 21 | "208.67.220.220", 22 | "208.67.222.222" 23 | ], 24 | "Quad9": [ 25 | "9.9.9.9", 26 | "149.112.112.112" 27 | ], 28 | "Yandex.DNS": [ 29 | "77.88.8.2", 30 | "77.88.8.88" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /config/hostapd.conf: -------------------------------------------------------------------------------- 1 | driver=nl80211 2 | ctrl_interface=/var/run/hostapd 3 | ctrl_interface_group=0 4 | beacon_int=100 5 | auth_algs=1 6 | wpa_key_mgmt=WPA-PSK 7 | ssid=RaspAP 8 | channel=1 9 | hw_mode=g 10 | wpa_passphrase=ChangeMe 11 | interface=wlan0 12 | wpa=2 13 | wpa_pairwise=CCMP 14 | country_code=GB 15 | ## Rapberry Pi 3 specific to on board WLAN/WiFi 16 | #ieee80211n=1 # 802.11n support (Raspberry Pi 3) 17 | #wmm_enabled=1 # QoS support (Raspberry Pi 3) 18 | #ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40] # (Raspberry Pi 3) 19 | 20 | ## RaspAP wireless client AP mode 21 | #interface=uap0 22 | 23 | ## RaspAP bridge AP mode, disabled by default 24 | #bridge=br0 25 | -------------------------------------------------------------------------------- /config/raspap-br0-member-eth0.network: -------------------------------------------------------------------------------- 1 | [Match] 2 | Name=eth0 3 | 4 | [Network] 5 | Bridge=br0 6 | -------------------------------------------------------------------------------- /config/raspap-bridge-br0.netdev: -------------------------------------------------------------------------------- 1 | [NetDev] 2 | Name=br0 3 | Kind=bridge 4 | -------------------------------------------------------------------------------- /config/raspap-bridge-br0.netplan: -------------------------------------------------------------------------------- 1 | network: 2 | version: 2 3 | renderer: networkd 4 | ethernets: 5 | eth0: 6 | dhcp4: no 7 | bridges: 8 | br0: 9 | dhcp4: yes 10 | interfaces: 11 | - eth0 12 | -------------------------------------------------------------------------------- /config/vpn-providers.json: -------------------------------------------------------------------------------- 1 | { 2 | "providers": [ 3 | { 4 | "id": 1, 5 | "name": "ExpressVPN", 6 | "bin_path": "/usr/bin/expressvpn", 7 | "install_page": "https://www.expressvpn.com/support/vpn-setup/app-for-linux/", 8 | "account_page": "https://www.expressvpn.com/subscriptions", 9 | "cmd_overrides": { 10 | "countries": "list all", 11 | "log": "diagnostics", 12 | "version": "-v" 13 | }, 14 | "regex": { 15 | "status": "\/not connected\/", 16 | "pattern": "\/^(.{2,5})\\s(?:.{1,28})(.{1,26}).*$\/", 17 | "replace": "$1,$2", 18 | "slice": 3 19 | } 20 | }, 21 | { 22 | "id": 2, 23 | "name": "Mullvad VPN", 24 | "bin_path": "/usr/bin/mullvad", 25 | "install_page": "https://mullvad.net/en/download/vpn/linux", 26 | "account_page": "https://mullvad.net/en/account", 27 | "cmd_overrides": { 28 | "account": "account get", 29 | "countries": "relay list", 30 | "log": "status -v", 31 | "version": "--version" 32 | }, 33 | "regex": { 34 | "status": "\/disconnected\/", 35 | "pattern": "\/^(.*),.*$\/", 36 | "replace": "$1" 37 | } 38 | }, 39 | { 40 | "id": 3, 41 | "name": "NordVPN", 42 | "bin_path": "/usr/bin/nordvpn", 43 | "install_page": "https://nordvpn.com/download/linux/", 44 | "account_page": "https://my.nordaccount.com/dashboard/", 45 | "cmd_overrides": { 46 | "log": "status" 47 | }, 48 | "regex": { 49 | "status": "\/status: disconnected\/", 50 | "pattern": "(\\w+)\\s+", 51 | "replace": "$1,$1\\n" 52 | } 53 | }, 54 | { 55 | "id": 4, 56 | "name": "AdGuard VPN", 57 | "bin_path": "/usr/local/bin/adguardvpn-cli", 58 | "install_page": "https://adguard-vpn.com/kb/adguard-vpn-for-linux/installation/", 59 | "account_page": "https://my.adguard-vpn.com/en/account/product/vpn", 60 | "cmd_overrides": { 61 | "countries": "list-locations", 62 | "connect": "connect -y -l", 63 | "log": "status", 64 | "account": "license", 65 | "version": "--version" 66 | }, 67 | "regex": { 68 | "status": "\/vpn is disconnected\/", 69 | "pattern": "/^([A-Z]{2})\\s+.*?\\s([A-Za-z]+(?:\\s[A-Za-z]+)?)\\s+\\d+$/m", 70 | "replace": "$2", 71 | "slice": 3 72 | } 73 | } 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /locale/en_US/LC_MESSAGES/messages.po 3 | translation: /locale/%locale_with_underscore%/LC_MESSAGES/%original_file_name% 4 | -------------------------------------------------------------------------------- /dist/datatables/dataTables.bootstrap4.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | DataTables Bootstrap 4 integration 3 | ©2011-2017 SpryMedia Ltd - datatables.net/license 4 | */ 5 | (function(b){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(a){return b(a,window,document)}):"object"===typeof exports?module.exports=function(a,d){a||(a=window);if(!d||!d.fn.dataTable)d=require("datatables.net")(a,d).$;return b(d,a,a.document)}:b(jQuery,window,document)})(function(b,a,d,m){var f=b.fn.dataTable;b.extend(!0,f.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", 6 | renderer:"bootstrap"});b.extend(f.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap4",sFilterInput:"form-control form-control-sm",sLengthSelect:"custom-select custom-select-sm form-control form-control-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"});f.ext.renderer.pageButton.bootstrap=function(a,h,r,s,j,n){var o=new f.Api(a),t=a.oClasses,k=a.oLanguage.oPaginate,u=a.oLanguage.oAria.paginate||{},e,g,p=0,q=function(d,f){var l,h,i,c,m=function(a){a.preventDefault(); 7 | !b(a.currentTarget).hasClass("disabled")&&o.page()!=a.data.action&&o.page(a.data.action).draw("page")};l=0;for(h=f.length;l", 8 | {"class":t.sPageButton+" "+g,id:0===r&&"string"===typeof c?a.sTableId+"_"+c:null}).append(b("",{href:"#","aria-controls":a.sTableId,"aria-label":u[c],"data-dt-idx":p,tabindex:a.iTabIndex,"class":"page-link"}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:c},m),p++)}},i;try{i=b(h).find(d.activeElement).data("dt-idx")}catch(v){}q(b(h).empty().html('