├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── webapp-dev.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── app.js ├── package.json ├── public ├── img │ ├── favicon │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── manifest.json │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ └── ms-icon-70x70.png │ └── logo.png ├── index.ejs ├── netbootxyz-web.ejs └── vendor │ ├── css │ ├── bootstrap.min.css │ ├── dataTables.bootstrap4.min.css │ └── docs.min.css │ └── js │ ├── ace.js │ ├── bootstrap.min.js │ ├── dataTables.bootstrap4.min.js │ ├── jquery.dataTables.min.js │ ├── jquery.min.js │ ├── mode-sh.js │ ├── popper.min.js │ └── theme-chrome.js ├── renovate.json └── root ├── defaults ├── default └── nginx.conf ├── donate.txt ├── etc └── supervisor.conf └── start.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: netbootxyz 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: '0' 17 | 18 | - name: Checkout docker-netbootxyz for container source files 19 | uses: actions/checkout@v4 20 | with: 21 | repository: netbootxyz/docker-netbootxyz 22 | path: docker-netbootxyz 23 | 24 | - name: Build the Docker image 25 | run: docker build . 26 | -------------------------------------------------------------------------------- /.github/workflows/webapp-dev.yml: -------------------------------------------------------------------------------- 1 | name: webapp-dev 2 | on: 3 | push 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v4 10 | 11 | - name: Checkout docker-netbootxyz for container source files 12 | uses: actions/checkout@v4 13 | with: 14 | repository: netbootxyz/docker-netbootxyz 15 | path: docker-netbootxyz 16 | 17 | - name: Set up Docker Buildx 18 | uses: docker/setup-buildx-action@v3 19 | 20 | - name: Login to the GitHub Container Registry 21 | uses: docker/login-action@v3 22 | with: 23 | registry: ghcr.io 24 | username: ${{ secrets.GHCR_USER }} 25 | password: ${{ secrets.GHCR_TOKEN }} 26 | 27 | - name: Build and push image 28 | uses: docker/build-push-action@v6 29 | with: 30 | push: true 31 | platforms: linux/amd64,linux/arm64 32 | context: . 33 | file: ./Dockerfile 34 | tags: | 35 | ghcr.io/netbootxyz/${{ github.workflow }}:latest 36 | ghcr.io/netbootxyz/${{ github.workflow }}:${{ github.sha }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .c9 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.21.3 2 | 3 | # set version label 4 | ARG BUILD_DATE 5 | ARG VERSION 6 | ARG WEBAPP_VERSION 7 | 8 | LABEL build_version="netboot.xyz version: ${VERSION} Build-date: ${BUILD_DATE}" 9 | LABEL maintainer="antonym" 10 | LABEL org.opencontainers.image.description="netboot.xyz official docker container - Your favorite operating systems in one place. A network-based bootable operating system installer based on iPXE." 11 | 12 | RUN \ 13 | apk update && \ 14 | apk upgrade && \ 15 | apk add --no-cache \ 16 | bash \ 17 | busybox \ 18 | curl \ 19 | envsubst \ 20 | git \ 21 | jq \ 22 | nghttp2-dev \ 23 | nginx \ 24 | nodejs \ 25 | shadow \ 26 | sudo \ 27 | supervisor \ 28 | syslog-ng \ 29 | tar \ 30 | dnsmasq && \ 31 | apk add --no-cache --virtual=build-dependencies \ 32 | npm && \ 33 | groupmod -g 1000 users && \ 34 | useradd -u 911 -U -d /config -s /bin/false nbxyz && \ 35 | usermod -G users nbxyz && \ 36 | mkdir /app \ 37 | /config \ 38 | /defaults 39 | 40 | COPY . /app 41 | 42 | RUN \ 43 | npm install --prefix /app && \ 44 | apk del --purge build-dependencies && \ 45 | rm -rf /tmp/* 46 | 47 | ENV TFTPD_OPTS='' 48 | ENV NGINX_PORT='80' 49 | ENV WEB_APP_PORT='3000' 50 | 51 | EXPOSE 69/udp 52 | EXPOSE 80 53 | EXPOSE 3000 54 | 55 | COPY docker-netbootxyz/root/ / 56 | 57 | # default command 58 | CMD ["sh","/start.sh"] 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netboot.xyz webapp 2 | 3 | This repo houses the netboot.xyz webapp that 4 | provides a web interface for editing iPXE files 5 | and downloading assets locally to the app. 6 | 7 | The app is versioned over time and is integrated into the docker-netbootxyz 8 | project located [here](https://github.com/netbootxyz/docker-netbootxyz). 9 | 10 | ## Building netboot.xyz webapp locally 11 | 12 | Uses the docker-netbootxyz repo for source files to avoid duplication of configs: 13 | 14 | ```bash 15 | git clone https://github.com/netbootxyz/webapp 16 | cd webapp 17 | git clone https://github.com/netbootxyz/docker-netbootxyz 18 | docker build . -t netbootxyz-webapp 19 | ``` 20 | 21 | ## Running it locally 22 | 23 | ```bash 24 | docker run -d \ 25 | --name=netbootxyz-webapp \ 26 | -e MENU_VERSION=2.0.84 `# optional` \ 27 | -p 3000:3000 `# sets webapp port` \ 28 | -p 69:69/udp `# sets tftp port` \ 29 | -p 8080:80 `# optional` \ 30 | -v /local/path/to/config:/config `# optional` \ 31 | -v /local/path/to/assets:/assets `# optional` \ 32 | --restart unless-stopped \ 33 | netbootxyz-webapp 34 | ``` 35 | 36 | * Port 3000: Web Application 37 | * Port 8080: NGINX Webserver for local asset hosting 38 | * Port 69: TFTP server for menus/kpxe files 39 | 40 | ## Running the latest webapp-dev build 41 | 42 | To run the build that contains the latest commited changes: 43 | 44 | ```bash 45 | docker run -d \ 46 | --name=netbootxyz-webapp-dev \ 47 | -e MENU_VERSION=2.0.84 `# optional` \ 48 | -p 3000:3000 `# sets webapp port` \ 49 | -p 69:69/udp `# sets tftp port` \ 50 | -p 8080:80 `# optional` \ 51 | -v /local/path/to/config:/config `# optional` \ 52 | -v /local/path/to/assets:/assets `# optional` \ 53 | --restart unless-stopped \ 54 | ghcr.io/netbootxyz/webapp-dev:latest 55 | ``` 56 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // netboot.xyz 2 | // Main Node.js app 3 | 4 | var baseurl = process.env.SUBFOLDER || '/'; 5 | var app = require('express')(); 6 | var { DownloaderHelper } = require('node-downloader-helper'); 7 | var exec = require('child_process').exec; 8 | var express = require('express'); 9 | var fs = require('fs'); 10 | var http = require('http').Server(app); 11 | var io = require('socket.io')(http, {path: baseurl + 'socket.io'}); 12 | var isBinaryFile = require("isbinaryfile").isBinaryFile; 13 | var path = require('path'); 14 | var readdirp = require('readdirp'); 15 | var fetch = require('node-fetch'); 16 | var urlLib = require('url'); 17 | 18 | const allowedHosts = [ 19 | 's3.amazonaws.com' 20 | ]; 21 | var si = require('systeminformation'); 22 | const util = require('util'); 23 | var { version } = require('./package.json'); 24 | var yaml = require('js-yaml'); 25 | var baserouter = express.Router(); 26 | let ejs = require('ejs'); 27 | 28 | // Disable sigs on every startup in remote boot.cfg 29 | disablesigs(); 30 | function disablesigs(){ 31 | var bootcfgr = '/config/menus/remote/boot.cfg'; 32 | var bootcfgl = '/config/menus/local/boot.cfg'; 33 | var bootcfgm = '/config/menus/boot.cfg'; 34 | if (fs.existsSync(bootcfgr) && ! fs.existsSync(bootcfgl)) { 35 | var data = fs.readFileSync(bootcfgr, 'utf8'); 36 | var disable = data.replace(/set sigs_enabled true/g, 'set sigs_enabled false'); 37 | fs.writeFileSync(bootcfgr, disable, 'utf8'); 38 | fs.writeFileSync(bootcfgm, disable, 'utf8'); 39 | } 40 | } 41 | 42 | ////// PATHS ////// 43 | //// Main //// 44 | baserouter.get("/", function (req, res) { 45 | res.render(__dirname + '/public/index.ejs', {baseurl: baseurl}); 46 | }); 47 | baserouter.get("/netbootxyz-web.js", function (req, res) { 48 | res.setHeader("Content-Type", "application/javascript"); 49 | res.render(__dirname + '/public/netbootxyz-web.ejs', {baseurl: baseurl}); 50 | }); 51 | //// Public JS and CSS //// 52 | baserouter.use('/public', express.static(__dirname + '/public')); 53 | 54 | // Socket IO connection 55 | io.on('connection', function(socket){ 56 | //// Socket Connect //// 57 | // Log Client and connection time 58 | console.log(socket.id + ' connected time=' + (new Date).getTime()); 59 | socket.join(socket.id); 60 | /////////////////////////// 61 | ////// Socket events ////// 62 | /////////////////////////// 63 | // When dashboard info is requested send to client 64 | socket.on('getdash', function(){ 65 | var tftpcmd = '/usr/sbin/dnsmasq --version | head -n1'; 66 | var nginxcmd = '/usr/sbin/nginx -v'; 67 | var dashinfo = {}; 68 | dashinfo['webversion'] = version; 69 | dashinfo['menuversion'] = fs.readFileSync('/config/menuversion.txt', 'utf8'); 70 | fetch('https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest', {headers: {'user-agent': 'node.js'}}) 71 | .then(response => { 72 | if (!response.ok) { 73 | throw new Error(`HTTP error! status: ${response.status}`); 74 | } 75 | return response.json(); 76 | }) 77 | .then(body => { 78 | dashinfo['remotemenuversion'] = body.tag_name; 79 | si.cpu(function(cpu) { 80 | dashinfo['cpu'] = cpu; 81 | si.mem(function(mem) { 82 | dashinfo['mem'] = mem; 83 | si.currentLoad(function(currentLoad) { 84 | dashinfo['CPUpercent'] = currentLoad.currentload_user; 85 | exec(tftpcmd, function (err, stdout) { 86 | dashinfo['tftpversion'] = stdout; 87 | exec(nginxcmd, function (err, stdout, stderr) { 88 | dashinfo['nginxversion'] = stderr; 89 | io.sockets.in(socket.id).emit('renderdash',dashinfo); 90 | }); 91 | }); 92 | }); 93 | }); 94 | }); 95 | }) 96 | .catch(error => { 97 | console.log('There was a problem with the fetch operation: ' + error.message); 98 | }); 99 | }); 100 | // When upgrade is requested run it 101 | socket.on('upgrademenus', function(version){ 102 | upgrademenu(version, function(response){ 103 | io.sockets.in(socket.id).emit('renderdashhook'); 104 | }); 105 | }); 106 | socket.on('upgrademenusdev', function(version){ 107 | upgrademenu(version, function(response){ 108 | io.sockets.in(socket.id).emit('renderconfighook'); 109 | }); 110 | }); 111 | // When config info is requested send file list to client 112 | socket.on('getconfig', function(){ 113 | var local_files = fs.readdirSync('/config/menus/local',{withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 114 | var remote_files = fs.readdirSync('/config/menus/remote',{withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 115 | io.sockets.in(socket.id).emit('renderconfig',remote_files,local_files); 116 | }); 117 | // When a file is requested send it's contents to the client 118 | socket.on('editgetfile', function(filename, islocal){ 119 | var rootDir = '/config/menus/'; 120 | var filePath = path.resolve(rootDir, filename); 121 | if (!filePath.startsWith(rootDir)) { 122 | io.sockets.in(socket.id).emit('error', 'Invalid file path'); 123 | return; 124 | } 125 | var data = fs.readFileSync(filePath); 126 | var stat = fs.lstatSync(filePath); 127 | isBinaryFile(data, stat.size).then((result) => { 128 | if (result) { 129 | io.sockets.in(socket.id).emit('editrenderfile','CANNOT EDIT THIS IS A BINARY FILE',filename,'nomenu'); 130 | } 131 | else { 132 | io.sockets.in(socket.id).emit('editrenderfile',data.toString("utf8"),filename,islocal); 133 | } 134 | }); 135 | }); 136 | // When save is requested save it sync files and return user to menu 137 | socket.on('saveconfig', function(filename, text){ 138 | var rootDir = '/config/menus/local/'; 139 | var filePath = path.resolve(rootDir, filename); 140 | if (!filePath.startsWith(rootDir)) { 141 | io.sockets.in(socket.id).emit('error', 'Invalid file path'); 142 | return; 143 | } 144 | fs.writeFileSync(filePath, text); 145 | layermenu(function(response){ 146 | var local_files = fs.readdirSync(rootDir, {withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 147 | var remote_files = fs.readdirSync('/config/menus/remote', {withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 148 | io.sockets.in(socket.id).emit('renderconfig', remote_files, local_files, filename, true); 149 | }); 150 | }); 151 | // When revert is requested delete it, sync files and return user to menu 152 | socket.on('revertconfig', function(filename){ 153 | var rootDir = '/config/menus/local/'; 154 | var filePath = path.resolve(rootDir, filename); 155 | if (!filePath.startsWith(rootDir)) { 156 | io.sockets.in(socket.id).emit('error', 'Invalid file path'); 157 | return; 158 | } 159 | fs.unlinkSync(filePath); 160 | layermenu(function(response){ 161 | var local_files = fs.readdirSync(rootDir, {withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 162 | var remote_files = fs.readdirSync('/config/menus/remote', {withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 163 | io.sockets.in(socket.id).emit('renderconfig', remote_files, local_files); 164 | }); 165 | }); 166 | // When a create file is 167 | socket.on('createipxe', function(filename){ 168 | var rootDir = '/config/menus/local/'; 169 | var filePath = path.resolve(rootDir, filename); 170 | if (!filePath.startsWith(rootDir)) { 171 | io.sockets.in(socket.id).emit('error', 'Invalid file path'); 172 | return; 173 | } 174 | fs.writeFileSync(filePath, '#!ipxe'); 175 | layermenu(function(response){ 176 | var local_files = fs.readdirSync(rootDir, {withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 177 | var remote_files = fs.readdirSync('/config/menus/remote', {withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 178 | io.sockets.in(socket.id).emit('renderconfig', remote_files, local_files, filename, true); 179 | }); 180 | }); 181 | // When the endpoints content is requested send it to the client 182 | socket.on('getlocal', async function(filename){ 183 | var remotemenuversion = fs.readFileSync('/config/menuversion.txt', 'utf8'); 184 | var endpointsfile = fs.readFileSync('/config/endpoints.yml'); 185 | var endpoints = yaml.load(endpointsfile); 186 | var localfiles = await readdirp.promise('/assets/.'); 187 | var assets = []; 188 | if (localfiles.length != 0){ 189 | for (var i in localfiles){ 190 | assets.push('/' + localfiles[i].path); 191 | } 192 | } 193 | io.sockets.in(socket.id).emit('renderlocal',endpoints,assets,remotemenuversion); 194 | }); 195 | // When remote downloads are requested make folders and download 196 | socket.on('dlremote', function(dlfiles){ 197 | dlremote(dlfiles, function(response){ 198 | io.sockets.in(socket.id).emit('renderlocalhook'); 199 | }); 200 | }); 201 | // When Local deletes are requested purge items 202 | socket.on('deletelocal', function(dlfiles){ 203 | for (var i in dlfiles){ 204 | var file = dlfiles[i]; 205 | fs.unlinkSync('/assets' + file); 206 | console.log('Deleted /assets' + file); 207 | if (fs.existsSync('/assets' + file + '.part2')) { 208 | fs.unlinkSync('/assets' + file + '.part2'); 209 | console.log('Deleted /assets' + file + '.part2'); 210 | } 211 | } 212 | io.sockets.in(socket.id).emit('renderlocalhook'); 213 | }); 214 | // When Dev Browser is requested reach out to github for versions 215 | socket.on('devgetbrowser', async function(){ 216 | var api_url = 'https://api.github.com/repos/netbootxyz/netboot.xyz/'; 217 | var options = {headers: {'user-agent': 'node.js'}}; 218 | var releasesResponse = await fetch(api_url + 'releases', options); 219 | if (!releasesResponse.ok) { 220 | throw new Error(`HTTP error! status: ${releasesResponse.status}`); 221 | } 222 | var releases = await releasesResponse.json(); 223 | var commitsResponse = await fetch(api_url + 'commits', options); 224 | if (!commitsResponse.ok) { 225 | throw new Error(`HTTP error! status: ${commitsResponse.status}`); 226 | } 227 | var commits = await commitsResponse.json() 228 | io.sockets.in(socket.id).emit('devrenderbrowser', releases, commits); 229 | }); 230 | }); 231 | 232 | 233 | //// Functions //// 234 | 235 | // Layer remote with local in the main tftp endpoint 236 | function layermenu(callback){ 237 | var local_files = fs.readdirSync('/config/menus/local',{withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 238 | var remote_files = fs.readdirSync('/config/menus/remote',{withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 239 | for (var i in remote_files){ 240 | var file = remote_files[i]; 241 | fs.copyFileSync('/config/menus/remote/' + file, '/config/menus/' + file); 242 | } 243 | for (var i in local_files){ 244 | var file = local_files[i]; 245 | fs.copyFileSync('/config/menus/local/' + file, '/config/menus/' + file); 246 | } 247 | callback(null, 'done'); 248 | } 249 | 250 | // Upgrade menus to specified version 251 | async function upgrademenu(version, callback){ 252 | var remote_folder = '/config/menus/remote/'; 253 | // Wipe current remote 254 | var remote_files = fs.readdirSync('/config/menus/remote',{withFileTypes: true}).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); 255 | for (var i in remote_files){ 256 | var file = remote_files[i]; 257 | fs.unlinkSync(remote_folder + file); 258 | } 259 | // Download files 260 | var downloads = []; 261 | var rom_files = ['netboot.xyz.kpxe', 262 | 'netboot.xyz-undionly.kpxe', 263 | 'netboot.xyz.efi', 264 | 'netboot.xyz-snp.efi', 265 | 'netboot.xyz-snponly.efi', 266 | 'netboot.xyz-arm64.efi', 267 | 'netboot.xyz-arm64-snp.efi', 268 | 'netboot.xyz-arm64-snponly.efi']; 269 | 270 | // This is a commit sha 271 | if (version.length == 40){ 272 | var download_endpoint = 'https://s3.amazonaws.com/dev.boot.netboot.xyz/' + version + '/ipxe/'; 273 | downloads.push({'url':'https://s3.amazonaws.com/dev.boot.netboot.xyz/' + version + '/menus.tar.gz','path':remote_folder}); 274 | } 275 | // This is a regular release 276 | else{ 277 | var download_endpoint = 'https://github.com/netbootxyz/netboot.xyz/releases/download/' + version + '/'; 278 | downloads.push({'url':download_endpoint + 'menus.tar.gz','path':remote_folder}); 279 | } 280 | for (var i in rom_files){ 281 | var file = rom_files[i]; 282 | var url = download_endpoint + file; 283 | downloads.push({'url':url,'path':remote_folder}); 284 | } 285 | // static config for endpoints 286 | downloads.push({'url':'https://raw.githubusercontent.com/netbootxyz/netboot.xyz/' + version +'/endpoints.yml','path':'/config/'}); 287 | await downloader(downloads); 288 | var untarcmd = 'tar xf ' + remote_folder + 'menus.tar.gz -C ' + remote_folder; 289 | if (version.length == 40){ 290 | var version = 'Development'; 291 | } 292 | exec(untarcmd, function (err, stdout) { 293 | fs.unlinkSync(remote_folder + 'menus.tar.gz'); 294 | fs.writeFileSync('/config/menuversion.txt', version); 295 | layermenu(function(response){ 296 | disablesigs(); 297 | callback(null, 'done'); 298 | }); 299 | }); 300 | } 301 | 302 | // Grab remote files 303 | async function dlremote(dlfiles, callback){ 304 | var dlarray = []; 305 | for (var i in dlfiles){ 306 | var dlfile = dlfiles[i]; 307 | var dlpath = '/assets' + path.dirname(dlfile); 308 | // Make destination directory 309 | fs.mkdirSync(dlpath, { recursive: true }); 310 | // Construct array for use in download function 311 | var url = 'https://github.com/netbootxyz' + dlfile; 312 | dlarray.push({'url':url,'path':dlpath}); 313 | } 314 | await downloader(dlarray); 315 | callback(null, 'done'); 316 | } 317 | 318 | // downloader loop 319 | async function downloader(downloads){ 320 | var startTime = new Date(); 321 | var total = downloads.length; 322 | for (var i in downloads){ 323 | var value = downloads[i]; 324 | var url = value.url; 325 | var path = value.path; 326 | var dloptions = {override:true,retry:{maxRetries:2,delay:5000}}; 327 | var dl = new DownloaderHelper(url, path, dloptions); 328 | 329 | dl.on('end', function(){ 330 | console.log('Downloaded ' + url + ' to ' + path); 331 | }); 332 | 333 | dl.on('error', function(error) { 334 | console.error('Download failed:', error); 335 | }); 336 | 337 | dl.on('progress', function(stats){ 338 | var currentTime = new Date(); 339 | var elaspsedTime = currentTime - startTime; 340 | if (elaspsedTime > 500) { 341 | startTime = currentTime; 342 | io.emit('dldata', url, [+i + 1,total], stats); 343 | } 344 | }); 345 | 346 | await dl.start().catch(error => { 347 | console.error('Download failed:', error); 348 | }); 349 | 350 | const parsedUrl = urlLib.parse(url); 351 | if (!allowedHosts.includes(parsedUrl.host)){ 352 | // Part 2 if exists repeat 353 | var response = await fetch(url + '.part2', {method: 'HEAD'}); 354 | var urltest = response.headers.get('server'); 355 | if (urltest == 'AmazonS3' || urltest == 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0') { 356 | var dl2 = new DownloaderHelper(url + '.part2', path, dloptions); 357 | dl2.on('end', function(){ 358 | console.log('Downloaded ' + url + '.part2' + ' to ' + path); 359 | }); 360 | dl2.on('progress', function(stats){ 361 | var currentTime = new Date(); 362 | var elaspsedTime = currentTime - startTime; 363 | if (elaspsedTime > 500) { 364 | startTime = currentTime; 365 | io.emit('dldata', url, [+i + 1,total], stats); 366 | } 367 | }); 368 | await dl2.start(); 369 | } 370 | } 371 | } 372 | io.emit('purgestatus'); 373 | } 374 | 375 | app.use(baseurl, baserouter); 376 | 377 | // Spin up application on port 3000 or set to WEB_APP_PORT env variable 378 | 379 | const defaultPort = 3000; 380 | 381 | let port = process.env.WEB_APP_PORT; 382 | 383 | if (!Number.isInteger(Number(port)) || port < 1 || port > 65535) { 384 | console.warn(`Invalid port "${port}" in environment variable WEB_APP_PORT. Using default port ${defaultPort} instead.`); 385 | port = defaultPort; 386 | } 387 | 388 | http.listen(port, function(){ 389 | console.log('listening on *:' + port); 390 | }); 391 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebApp", 3 | "version": "0.7.5", 4 | "description": "Configuration and mirroring application for netboot.xyz stack", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/netbootxyz/webapp.git" 12 | }, 13 | "author": "netboot.xyz", 14 | "license": "Apache-2.0", 15 | "homepage": "https://netboot.xyz", 16 | "dependencies": { 17 | "ejs": "3.1.10", 18 | "express": "4.21.2", 19 | "http": "0.0.0", 20 | "isbinaryfile": "5.0.4", 21 | "js-yaml": "4.1.0", 22 | "node-downloader-helper": "2.1.9", 23 | "readdirp": "3.6.0", 24 | "node-fetch": "2.7.0", 25 | "socket.io": "4.8.1", 26 | "systeminformation": "5.25.11" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /public/img/favicon/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/android-icon-144x144.png -------------------------------------------------------------------------------- /public/img/favicon/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/android-icon-192x192.png -------------------------------------------------------------------------------- /public/img/favicon/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/android-icon-36x36.png -------------------------------------------------------------------------------- /public/img/favicon/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/android-icon-48x48.png -------------------------------------------------------------------------------- /public/img/favicon/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/android-icon-72x72.png -------------------------------------------------------------------------------- /public/img/favicon/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/android-icon-96x96.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-114x114.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-120x120.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-144x144.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-57x57.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-60x60.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-72x72.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-76x76.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon-precomposed.png -------------------------------------------------------------------------------- /public/img/favicon/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/apple-icon.png -------------------------------------------------------------------------------- /public/img/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /public/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/favicon.ico -------------------------------------------------------------------------------- /public/img/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | {"name":"App","icons":[{"src":"/android-icon-36x36.png","sizes":"36x36","type":"image/png"},{"src":"/android-icon-48x48.png","sizes":"48x48","type":"image/png"},{"src":"/android-icon-72x72.png","sizes":"48x48","type":"image/png"},{"src":"/android-icon-96x96.png","sizes":"120x120","type":"image/png"},{"src":"/android-icon-144x144.png","sizes":"144x144","type":"image/png"},{"src":"/android-icon-192x192.png","sizes":"192x192","type":"image/png"}]} -------------------------------------------------------------------------------- /public/img/favicon/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/ms-icon-144x144.png -------------------------------------------------------------------------------- /public/img/favicon/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/ms-icon-150x150.png -------------------------------------------------------------------------------- /public/img/favicon/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/ms-icon-310x310.png -------------------------------------------------------------------------------- /public/img/favicon/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/favicon/ms-icon-70x70.png -------------------------------------------------------------------------------- /public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netbootxyz/webapp/44c26959872ba08fe7f082f48beef17b5b3d7c7d/public/img/logo.png -------------------------------------------------------------------------------- /public/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | netboot.xyz Configuration 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 | 60 | 61 |
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /public/netbootxyz-web.ejs: -------------------------------------------------------------------------------- 1 | // netboot.xyz 2 | // Client side javascript 3 | 4 | 5 | // Initiate a websocket connection to the server 6 | var host = window.location.hostname; 7 | var port = window.location.port; 8 | var protocol = window.location.protocol; 9 | var socket = io.connect(protocol + '//' + host + ':' + port, {path: "<%= baseurl %>socket.io"}); 10 | // If the page is being loaded for the first time render in the homepage 11 | $(document).ready(function(){renderdash()}) 12 | 13 | 14 | //// Dashboard Page rendering //// 15 | function renderdash(){ 16 | $('#pagecontent').empty(); 17 | $('#pagecontent').append('
Loading...

Getting Dashboard

'); 18 | socket.emit('getdash'); 19 | } 20 | socket.on('renderdash', function(response){ 21 | var tftpversion = response.tftpversion; 22 | var nginxversion = response.nginxversion; 23 | var webversion = response.webversion; 24 | var menuversion = response.menuversion; 25 | var remotemenuversion = response.remotemenuversion; 26 | var cpustats = response.cpu; 27 | var cpupercent = response.CPUpercent; 28 | var memstats = response.mem; 29 | var usedmem = (memstats.active/memstats.total)*100; 30 | var totalmem = parseFloat(memstats.total/1000000000).toFixed(2); 31 | var diskbuffer = parseFloat(memstats.buffcache/1000000000).toFixed(2); 32 | if (menuversion != remotemenuversion){ 33 | var upgradebutton = '' 34 | } 35 | else{ 36 | var upgradebutton = '' 37 | } 38 | $('#pagecontent').empty(); 39 | $('#pagecontent').append('\ 40 |
\ 41 |
\ 42 | Software and Services\ 43 |
\ 44 |
\ 45 |
\ 46 |
\ 47 | Versions\ 48 |
\ 49 |
\ 50 | \ 51 | \ 52 | \ 53 | \ 54 |
Webapp Version: ' + webversion + '
Menus Version:' + menuversion + '
Upgrade Menus to latest
' + upgradebutton + '
\ 55 |
\ 56 |
\ 57 |
\ 58 |
\ 59 | Services\ 60 |
\ 61 |
\ 62 | \ 63 | \ 64 | \ 65 |
TFTP:' + tftpversion + '
WebServer:' + nginxversion + '
\ 66 |
\ 67 |
\ 68 |
\ 69 |
\ 70 |
\ 71 |
\ 72 | System Stats\ 73 |
\ 74 |
\ 75 |
\ 76 |
\ 77 | CPU\ 78 |
\ 79 |
\ 80 | \ 81 | \ 82 | \ 83 | \ 84 |
CPU' + cpustats.manufacturer + ' ' + cpustats.brand + '
Cores' + cpustats.cores + '
Usage
\ 85 |
\ 86 |
\ 87 |
\ 88 |
\ 89 | Memory\ 90 |
\ 91 |
\ 92 | \ 93 | \ 94 | \ 95 | \ 96 |
Total Mem' + totalmem + 'G
Disk buffer' + diskbuffer + 'G
Usage
\ 97 |
\ 98 |
\ 99 |
\ 100 |
\ 101 | '); 102 | }); 103 | // Upgrade menu files 104 | function upgrademenus(version){ 105 | $('#upgradebutton').empty(); 106 | $('#upgradebutton').append('
Loading...
'); 107 | socket.emit('upgrademenus', version); 108 | } 109 | function upgrademenusdev(version){ 110 | $('#configcontent').empty(); 111 | $('#configcontent').append('
Loading...

Pulling menus at version requested

'); 112 | socket.emit('upgrademenusdev', version); 113 | } 114 | // Re-render dash hook 115 | socket.on('renderdashhook', function(){ 116 | if($('#upgradebutton').length){ 117 | renderdash(); 118 | } 119 | }); 120 | 121 | //// Config Page rendering //// 122 | function renderconfig(){ 123 | $('#pagecontent').empty(); 124 | $('#pagecontent').append('
Loading...

Getting Config Items

'); 125 | socket.emit('getconfig'); 126 | } 127 | socket.on('renderconfig', function(remote_files,local_files,filename,islocal){ 128 | $('#pagecontent').empty(); 129 | $('#pagecontent').append('\ 130 |
\ 131 |
\ 132 |
\ 133 | \ 139 |
\ 141 |
\ 142 |
\ 143 |

Please choose a file to edit
Or

\ 144 |
\ 145 |
\ 146 |
\ 147 |
\ 148 |
\ 149 |

\ 150 |
\ 151 |
\ 152 |
\ 153 |
\ 154 |
\ 155 |
\ 156 |
\ 157 |
\ 158 |
'); 159 | $(local_files).each(function( index, value ) { 160 | $('#bd-docs-nav').append('\ 161 |
\ 162 | \ 165 |
'); 166 | }).promise().done(function(){ 167 | $(remote_files).each(function( index, value ) { 168 | if (! local_files.includes(value)){ 169 | $('#bd-docs-nav').append('\ 170 |
\ 171 | \ 174 |
'); 175 | } 176 | }).promise().done(function(){ 177 | if (filename){ 178 | socket.emit('editgetfile',filename,islocal); 179 | } 180 | }); 181 | }); 182 | }); 183 | // Render edit window 184 | function editgetfile(filename,islocal){ 185 | $('#configcontent').empty(); 186 | $('#configcontent').append('
Loading...

Getting File Contents

'); 187 | socket.emit('editgetfile',filename,islocal); 188 | } 189 | socket.on('editrenderfile', function(response,filename,metadata){ 190 | // Filter the buttons to display based on type 191 | if (metadata == 'nomenu'){ 192 | var buttons = ''; 193 | } 194 | else if (metadata == false){ 195 | var buttons = ''; 196 | } 197 | else if (metadata == true){ 198 | var buttons = '\ 199 | '; 200 | } 201 | $('#configcontent').empty(); 202 | $('#configcontent').append('\ 203 |
\ 204 |
\ 205 |
\ 206 |

' + filename + '

\ 207 |
\ 208 |
\ 209 | ' + buttons + '\ 210 |
\ 211 |
\ 212 |
\ 213 |
'); 214 | editor = ace.edit('editor'); 215 | editor.setTheme('ace/theme/chrome'); 216 | editor.session.setMode('ace/mode/sh'); 217 | editor.$blockScrolling = Infinity; 218 | editor.setOptions({ 219 | readOnly: false, 220 | }); 221 | editor.setValue(response, -1); 222 | }); 223 | // Save users file 224 | function saveconfig(filename){ 225 | var editor = ace.edit("editor"); 226 | var text = editor.getValue(); 227 | socket.emit('saveconfig',filename,text); 228 | $('#pagecontent').empty(); 229 | $('#pagecontent').append('
Loading...

Saving File

'); 230 | } 231 | // Delete a local file (revert) 232 | function revertconfig(filename){ 233 | socket.emit('revertconfig',filename); 234 | $('#pagecontent').empty(); 235 | $('#pagecontent').append('
Loading...

Reverting File

'); 236 | } 237 | // Create a new file 238 | function createipxe(){ 239 | var filename = $('.ipxefilename').val().trim(); 240 | if (filename){ 241 | socket.emit('createipxe',filename); 242 | $('#pagecontent').empty(); 243 | $('#pagecontent').append('
Loading...

Creating File

'); 244 | } 245 | } 246 | // Render edit window 247 | function devbrowser(){ 248 | $('#configcontent').empty(); 249 | $('#configcontent').append('
Loading...

Getting Remote Development Versions

'); 250 | socket.emit('devgetbrowser'); 251 | } 252 | socket.on('devrenderbrowser', function(releases,commits){ 253 | $('#configcontent').empty(); 254 | $('#configcontent').append('\ 255 |
\ 256 |
\ 257 |
\ 258 | Development Commits\ 259 |
\ 260 |
\ 261 | \ 262 | \ 263 | \ 264 | \ 265 | \ 266 | \ 267 | \ 268 |
Commit
\ 269 |
\ 270 |
\ 271 |
\ 272 |
\ 273 | Releases\ 274 |
\ 275 |
\ 276 | \ 277 | \ 278 | \ 279 | \ 280 | \ 281 | \ 282 | \ 283 |
Release
\ 284 |
\ 285 |
\ 286 |
'); 287 | var tableoptions = { 288 | "paging": false, 289 | "bInfo" : false, 290 | 'sDom': 't', 291 | "order": [] 292 | }; 293 | $("#commits").dataTable().fnDestroy(); 294 | $("#releases").dataTable().fnDestroy(); 295 | var commitstable = $('#commits').DataTable(tableoptions); 296 | var releasestable = $('#releases').DataTable(tableoptions); 297 | commitstable.clear(); 298 | releasestable.clear(); 299 | $.each(releases, function(index,value){ 300 | releasestable.row.add( 301 | [ 302 | '' + value.tag_name + '', 303 | '' 304 | ] 305 | ); 306 | }); 307 | $.each(commits, function(index,value){ 308 | commitstable.row.add( 309 | [ 310 | '' + value.sha + '', 311 | '' 312 | ] 313 | ); 314 | }); 315 | commitstable.draw(); 316 | releasestable.draw(); 317 | }); 318 | // Re-render menus hook 319 | socket.on('renderconfighook', function(){ 320 | if($('#bd-docs-nav').length){ 321 | renderconfig(); 322 | } 323 | }); 324 | 325 | //// Local rendering //// 326 | function renderlocal(){ 327 | $('#pagecontent').empty(); 328 | $('#pagecontent').append('
Loading...

Getting Remote file list

'); 329 | socket.emit('getlocal'); 330 | } 331 | socket.on('renderlocal', function(endpoints,localfiles,remotemenuversion){ 332 | $('#pagecontent').empty(); 333 | $('#pagecontent').append('\ 334 |
\ 335 |
\ 336 |
\ 337 | Remote Assets at ' + remotemenuversion + '\ 338 | \ 339 |
\ 340 |
\ 341 |
\ 342 |
\ 343 |
\ 344 |
\ 345 |
\ 346 |
\ 347 |
\ 348 | \ 349 | \ 350 | \ 351 | \ 352 | \ 353 | \ 354 | \ 355 | \ 356 |
Asset NameAsset path
\ 357 |
\ 358 |
\ 359 |
\ 360 |
\ 361 | Local Assets\ 362 | \ 363 |
\ 364 |
\ 365 |
\ 366 |
\ 367 |
\ 368 |
\ 369 |
\ 370 |
\ 371 |
\ 372 | \ 373 | \ 374 | \ 375 | \ 376 | \ 377 | \ 378 | \ 379 | \ 380 |
Asset NameAsset path
\ 381 | \ 382 | \ 383 | \ 384 | \ 385 | \ 386 | \ 387 | \ 388 |
Untracked Assets
\ 389 |
\ 390 |
\ 391 |
'); 392 | var tableoptions = { 393 | "paging": false, 394 | "bInfo" : false, 395 | 'sDom': 't', 396 | "order": [[ 0, "asc" ]] 397 | }; 398 | $("#localassets").dataTable().fnDestroy(); 399 | $("#remoteassets").dataTable().fnDestroy(); 400 | var localtable = $('#localassets').DataTable(tableoptions); 401 | var remotetable = $('#remoteassets').DataTable(tableoptions); 402 | localtable.clear(); 403 | remotetable.clear(); 404 | $.each(endpoints.endpoints, function(index,value){ 405 | $.each(value.files, function( arrindex, file ) { 406 | if (localfiles.includes(value.path + file)){ 407 | localtable.row.add( 408 | [ 409 | index, 410 | value.path.split('download/')[1] + file, 411 | '' 412 | 413 | ] 414 | ); 415 | localfiles.splice( localfiles.indexOf(value.path + file), 1 ); 416 | } 417 | else{ 418 | remotetable.row.add( 419 | [ 420 | index, 421 | '' + value.path.split('download/')[1] + file + '', 422 | '' 423 | ] 424 | ); 425 | } 426 | }); 427 | }); 428 | if (localfiles.length != 0){ 429 | var untrackedtable = $('#untrackedassets').DataTable(tableoptions); 430 | $.each(localfiles, function( arrindex, file ) { 431 | if (!file.endsWith('.part2')) { 432 | untrackedtable.row.add( 433 | [ 434 | '/assets' + file, 435 | '' 436 | ] 437 | ); 438 | } 439 | }); 440 | untrackedtable.draw(); 441 | } 442 | remotetable.draw(); 443 | localtable.draw(); 444 | $('#localsearch').keyup(function(){ 445 | localtable.search($(this).val()).draw() ; 446 | }) 447 | $('#remotesearch').keyup(function(){ 448 | remotetable.search($(this).val()).draw() ; 449 | }) 450 | }); 451 | function remoteselect(){ 452 | $('.remotecheck').each(function() { 453 | if (this.style.display != "none"){ 454 | this.checked = true; 455 | } 456 | }); 457 | }; 458 | function remoteclear(){ 459 | $('.remotecheck').each(function() { 460 | this.checked = false; 461 | }); 462 | }; 463 | function localselect(){ 464 | $('.localcheck').each(function() { 465 | this.checked = true; 466 | }); 467 | }; 468 | function localclear(){ 469 | $('.localcheck').each(function() { 470 | this.checked = false; 471 | }); 472 | }; 473 | // Download remote files 474 | function dlremote(){ 475 | var allfiles = $('.remotecheck'); 476 | var dlfiles = []; 477 | $.each(allfiles, function( index, value ) { 478 | if($(this).is(":checked")){ 479 | dlfiles.push($(this).val()); 480 | } 481 | }).promise().done(function(){ 482 | if(dlfiles.length != 0){ 483 | socket.emit('dlremote',dlfiles); 484 | } 485 | }); 486 | } 487 | // Re-render local hook 488 | socket.on('renderlocalhook', function(){ 489 | if($('#localassets').length){ 490 | renderlocal(); 491 | } 492 | }); 493 | // Delete local files 494 | function deletelocal(){ 495 | var allfiles = $('.localcheck'); 496 | var deletefiles = []; 497 | $.each(allfiles, function( index, value ) { 498 | if($(this).is(":checked")){ 499 | deletefiles.push($(this).val()); 500 | } 501 | }).promise().done(function(){ 502 | if(deletefiles.length != 0){ 503 | socket.emit('deletelocal',deletefiles); 504 | } 505 | }); 506 | } 507 | 508 | //// Download Status Bars //// 509 | socket.on('dldata', function(url, count, stats){ 510 | $('#statusbar').empty(); 511 | $('#statusbar').append('\ 512 |
\ 513 |
\ 514 | ' + url.split('download/')[1] + '\ 515 |
\ 516 |
\ 517 | ' + count[0] + ' of ' + count[1] + '\ 518 |
\ 519 |
\ 520 |
\ 521 |
\ 522 |
\ 523 |
\ 524 | '); 525 | }); 526 | socket.on('purgestatus', function(){ 527 | $('#statusbar').empty(); 528 | }); 529 | -------------------------------------------------------------------------------- /public/vendor/css/dataTables.bootstrap4.min.css: -------------------------------------------------------------------------------- 1 | table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important;border-collapse:separate !important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:auto;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:normal;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:0.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:0.85em;white-space:nowrap}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap;justify-content:flex-end}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_desc,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>td.sorting{padding-right:30px}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable thead .sorting:before,table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:before,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:before,table.dataTable thead .sorting_desc_disabled:after{position:absolute;bottom:0.9em;display:block;opacity:0.3}table.dataTable thead .sorting:before,table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_desc:before,table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_desc_disabled:before{right:1em;content:"\2191"}table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{right:0.5em;content:"\2193"}table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_desc:after{opacity:1}table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_desc_disabled:after{opacity:0}div.dataTables_scrollHead table.dataTable{margin-bottom:0 !important}div.dataTables_scrollBody table{border-top:none;margin-top:0 !important;margin-bottom:0 !important}div.dataTables_scrollBody table thead .sorting:before,div.dataTables_scrollBody table thead .sorting_asc:before,div.dataTables_scrollBody table thead .sorting_desc:before,div.dataTables_scrollBody table thead .sorting:after,div.dataTables_scrollBody table thead .sorting_asc:after,div.dataTables_scrollBody table thead .sorting_desc:after{display:none}div.dataTables_scrollBody table tbody tr:first-child th,div.dataTables_scrollBody table tbody tr:first-child td{border-top:none}div.dataTables_scrollFoot>.dataTables_scrollFootInner{box-sizing:content-box}div.dataTables_scrollFoot>.dataTables_scrollFootInner>table{margin-top:0 !important;border-top:none}@media screen and (max-width: 767px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}}table.dataTable.table-sm>thead>tr>th{padding-right:20px}table.dataTable.table-sm .sorting:before,table.dataTable.table-sm .sorting_asc:before,table.dataTable.table-sm .sorting_desc:before{top:5px;right:0.85em}table.dataTable.table-sm .sorting:after,table.dataTable.table-sm .sorting_asc:after,table.dataTable.table-sm .sorting_desc:after{top:5px}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable td:last-child{border-right-width:0}table.table-bordered.dataTable tbody th,table.table-bordered.dataTable tbody td{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^="col-"]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^="col-"]:last-child{padding-right:0} 2 | -------------------------------------------------------------------------------- /public/vendor/css/docs.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Docs (https://getbootstrap.com) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under the Creative Commons Attribution 3.0 Unported License. For 6 | * details, see https://creativecommons.org/licenses/by/3.0/. 7 | */ 8 | 9 | .bd-toc { 10 | -webkit-box-ordinal-group: 3; 11 | -ms-flex-order: 2; 12 | order: 2; 13 | padding-top: 1.5rem; 14 | padding-bottom: 1.5rem; 15 | font-size: .875rem 16 | } 17 | 18 | @supports ((position:-webkit-sticky) or (position:sticky)) { 19 | .bd-toc { 20 | position: -webkit-sticky; 21 | position: sticky; 22 | top: 4rem; 23 | height: calc(100vh - 4rem); 24 | overflow-y: auto 25 | } 26 | } 27 | 28 | 29 | .toc-entry { 30 | display: block 31 | } 32 | 33 | .toc-entry a { 34 | display: block; 35 | padding: .125rem 1.5rem; 36 | color: #99979c 37 | } 38 | 39 | .toc-entry a:hover { 40 | color: #007bff; 41 | text-decoration: none 42 | } 43 | 44 | .bd-sidebar { 45 | -webkit-box-ordinal-group: 1; 46 | -ms-flex-order: 0; 47 | order: 0; 48 | border-bottom: 1px solid rgba(0, 0, 0, .1) 49 | } 50 | 51 | @media (min-width:768px) { 52 | .bd-sidebar { 53 | border-right: 1px solid rgba(0, 0, 0, .1) 54 | } 55 | @supports ((position:-webkit-sticky) or (position:sticky)) { 56 | .bd-sidebar { 57 | position: -webkit-sticky; 58 | position: sticky; 59 | top: 4rem; 60 | z-index: 1000; 61 | height: calc(100vh - 4rem) 62 | } 63 | } 64 | } 65 | 66 | @media (min-width:1200px) { 67 | .bd-sidebar { 68 | -webkit-box-flex: 0; 69 | -ms-flex: 0 1 320px; 70 | flex: 0 1 320px 71 | } 72 | } 73 | 74 | .bd-links { 75 | padding-top: 1rem; 76 | padding-bottom: 1rem; 77 | margin-right: -15px; 78 | margin-left: -15px 79 | } 80 | 81 | @media (min-width:768px) { 82 | @supports ((position: -webkit-sticky) or (position:sticky)) { 83 | .bd-links { 84 | max-height:calc(100vh - 9rem); 85 | overflow-y: auto 86 | } 87 | } 88 | } 89 | 90 | @media (min-width:768px) { 91 | .bd-links { 92 | display: block!important 93 | } 94 | } 95 | 96 | 97 | .bd-search-docs-toggle { 98 | line-height: 1; 99 | color: #212529 100 | } 101 | 102 | .bd-sidenav { 103 | display: none 104 | } 105 | 106 | .bd-toc-link { 107 | display: block; 108 | padding: .25rem 1.5rem; 109 | font-weight: 500; 110 | color: rgba(0, 0, 0, .65) 111 | } 112 | 113 | .bd-toc-link:hover { 114 | color: rgba(0, 0, 0, .85); 115 | text-decoration: none 116 | } 117 | 118 | .bd-toc-item.active { 119 | margin-bottom: 1rem 120 | } 121 | 122 | .bd-toc-item.active:not(:first-child) { 123 | margin-top: 1rem 124 | } 125 | 126 | .bd-toc-item.active>.bd-toc-link { 127 | color: rgba(0, 0, 0, .85) 128 | } 129 | 130 | .bd-toc-item.active>.bd-toc-link:hover { 131 | background-color: transparent 132 | } 133 | 134 | .bd-toc-item.active>.bd-sidenav { 135 | display: block 136 | } 137 | 138 | .bd-sidebar .nav>li>a { 139 | display: block; 140 | padding: .25rem 1.5rem; 141 | font-size: 90%; 142 | color: rgba(0, 0, 0, .65) 143 | } 144 | 145 | .bd-sidebar .nav>li>a:hover { 146 | color: rgba(0, 0, 0, .85); 147 | text-decoration: none; 148 | background-color: transparent 149 | } 150 | 151 | .bd-sidebar .nav>.active:hover>a, 152 | .bd-sidebar .nav>.active>a { 153 | font-weight: 500; 154 | color: rgba(0, 0, 0, .85); 155 | background-color: transparent 156 | } -------------------------------------------------------------------------------- /public/vendor/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v4.4.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Y.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Se,popperConfig:null},Fe="show",Ue="out",We={HIDE:"hide"+Oe,HIDDEN:"hidden"+Oe,SHOW:"show"+Oe,SHOWN:"shown"+Oe,INSERTED:"inserted"+Oe,CLICK:"click"+Oe,FOCUSIN:"focusin"+Oe,FOCUSOUT:"focusout"+Oe,MOUSEENTER:"mouseenter"+Oe,MOUSELEAVE:"mouseleave"+Oe},qe="fade",Me="show",Ke=".tooltip-inner",Qe=".arrow",Be="hover",Ve="focus",Ye="click",ze="manual",Xe=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Me))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(qe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,this._getPopperConfig(a)),g(o).addClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===Ue&&e._leave(null,e)};if(g(this.tip).hasClass(qe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){function e(){n._hoverState!==Fe&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),g(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()}var n=this,i=this.getTipElement(),o=g.Event(this.constructor.Event.HIDE);if(g(this.element).trigger(o),!o.isDefaultPrevented()){if(g(i).removeClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ye]=!1,this._activeTrigger[Ve]=!1,this._activeTrigger[Be]=!1,g(this.tip).hasClass(qe)){var r=_.getTransitionDurationFromElement(i);g(i).one(_.TRANSITION_END,e).emulateTransitionEnd(r)}else e();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Pe+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ke)),this.getTitle()),g(t).removeClass(qe+" "+Me)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=we(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t=t||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},t._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Qe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},{},this.config.popperConfig)},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,{},e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Re[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==ze){var e=t===Be?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Be?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),this._hideModalHandler=function(){i.element&&i.hide()},g(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==t||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Ve:Be]=!0),g(e.getTipElement()).hasClass(Me)||e._hoverState===Fe?e._hoverState=Fe:(clearTimeout(e._timeout),e._hoverState=Fe,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Fe&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Ve:Be]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ue,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ue&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==je.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,{},e,{},"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(Ae,t,this.constructor.DefaultType),t.sanitize&&(t.template=we(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Le);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(qe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ne),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ne,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return Ae}},{key:"DATA_KEY",get:function(){return Ne}},{key:"Event",get:function(){return We}},{key:"EVENT_KEY",get:function(){return Oe}},{key:"DefaultType",get:function(){return He}}]),i}();g.fn[Ae]=Xe._jQueryInterface,g.fn[Ae].Constructor=Xe,g.fn[Ae].noConflict=function(){return g.fn[Ae]=ke,Xe._jQueryInterface};var $e="popover",Ge="bs.popover",Je="."+Ge,Ze=g.fn[$e],tn="bs-popover",en=new RegExp("(^|\\s)"+tn+"\\S+","g"),nn=l({},Xe.Default,{placement:"right",trigger:"click",content:"",template:''}),on=l({},Xe.DefaultType,{content:"(string|element|function)"}),rn="fade",sn="show",an=".popover-header",ln=".popover-body",cn={HIDE:"hide"+Je,HIDDEN:"hidden"+Je,SHOW:"show"+Je,SHOWN:"shown"+Je,INSERTED:"inserted"+Je,CLICK:"click"+Je,FOCUSIN:"focusin"+Je,FOCUSOUT:"focusout"+Je,MOUSEENTER:"mouseenter"+Je,MOUSELEAVE:"mouseleave"+Je},hn=function(t){function i(){return t.apply(this,arguments)||this}!function(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}(i,t);var e=i.prototype;return e.isWithContent=function(){return this.getTitle()||this._getContent()},e.addAttachmentClass=function(t){g(this.getTipElement()).addClass(tn+"-"+t)},e.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},e.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(an),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ln),e),t.removeClass(rn+" "+sn)},e._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},e._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(en);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<'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>>", 9 | renderer:"bootstrap"});a.extend(d.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"});d.ext.renderer.pageButton.bootstrap=function(b,l,v,w,m,r){var k=new d.Api(b),x=b.oClasses,n=b.oLanguage.oPaginate,y=b.oLanguage.oAria.paginate||{},g,h,t=0,u=function(c,d){var e,l=function(b){b.preventDefault(); 10 | a(b.currentTarget).hasClass("disabled")||k.page()==b.data.action||k.page(b.data.action).draw("page")};var q=0;for(e=d.length;q",{"class":x.sPageButton+" "+h,id:0===v&&"string"===typeof f?b.sTableId+"_"+f:null}).append(a("",{href:"#","aria-controls":b.sTableId,"aria-label":y[f],"data-dt-idx":t,tabindex:b.iTabIndex,"class":"page-link"}).html(g)).appendTo(c);b.oApi._fnBindAction(p,{action:f},l);t++}}}};try{var p=a(l).find(c.activeElement).data("dt-idx")}catch(z){}u(a(l).empty().html('
    ').children("ul"),w);p!==e&&a(l).find("[data-dt-idx="+p+"]").focus()};return d}); 12 | -------------------------------------------------------------------------------- /public/vendor/js/mode-sh.js: -------------------------------------------------------------------------------- 1 | define("ace/mode/sh_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.reservedKeywords="!|{|}|case|do|done|elif|else|esac|fi|for|if|in|then|until|while|&|;|export|local|read|typeset|unset|elif|select|set|function|declare|readonly",o=t.languageConstructs="[|]|alias|bg|bind|break|builtin|cd|command|compgen|complete|continue|dirs|disown|echo|enable|eval|exec|exit|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|popd|printf|pushd|pwd|return|set|shift|shopt|source|suspend|test|times|trap|type|ulimit|umask|unalias|wait",u=function(){var e=this.createKeywordMapper({keyword:s,"support.function.builtin":o,"invalid.deprecated":"debugger"},"identifier"),t="(?:(?:[1-9]\\d*)|(?:0))",n="(?:\\.\\d+)",r="(?:\\d+)",i="(?:(?:"+r+"?"+n+")|(?:"+r+"\\.))",u="(?:(?:"+i+"|"+r+")"+")",a="(?:"+u+"|"+i+")",f="(?:&"+r+")",l="[a-zA-Z_][a-zA-Z0-9_]*",c="(?:"+l+"(?==))",h="(?:\\$(?:SHLVL|\\$|\\!|\\?))",p="(?:"+l+"\\s*\\(\\))";this.$rules={start:[{token:"constant",regex:/\\./},{token:["text","comment"],regex:/(^|\s)(#.*)$/},{token:"string.start",regex:'"',push:[{token:"constant.language.escape",regex:/\\(?:[$`"\\]|$)/},{include:"variables"},{token:"keyword.operator",regex:/`/},{token:"string.end",regex:'"',next:"pop"},{defaultToken:"string"}]},{token:"string",regex:"\\$'",push:[{token:"constant.language.escape",regex:/\\(?:[abeEfnrtv\\'"]|x[a-fA-F\d]{1,2}|u[a-fA-F\d]{4}([a-fA-F\d]{4})?|c.|\d{1,3})/},{token:"string",regex:"'",next:"pop"},{defaultToken:"string"}]},{regex:"<<<",token:"keyword.operator"},{stateName:"heredoc",regex:"(<<-?)(\\s*)(['\"`]?)([\\w\\-]+)(['\"`]?)",onMatch:function(e,t,n){var r=e[2]=="-"?"indentedHeredoc":"heredoc",i=e.split(this.splitRegex);return n.push(r,i[4]),[{type:"constant",value:i[1]},{type:"text",value:i[2]},{type:"string",value:i[3]},{type:"support.class",value:i[4]},{type:"string",value:i[5]}]},rules:{heredoc:[{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}],indentedHeredoc:[{token:"string",regex:"^ +"},{onMatch:function(e,t,n){return e===n[1]?(n.shift(),n.shift(),this.next=n[0]||"start","support.class"):(this.next="","string")},regex:".*$",next:"start"}]}},{regex:"$",token:"empty",next:function(e,t){return t[0]==="heredoc"||t[0]==="indentedHeredoc"?t[0]:e}},{token:["keyword","text","text","text","variable"],regex:/(declare|local|readonly)(\s+)(?:(-[fixar]+)(\s+))?([a-zA-Z_][a-zA-Z0-9_]*\b)/},{token:"variable.language",regex:h},{token:"variable",regex:c},{include:"variables"},{token:"support.function",regex:p},{token:"support.function",regex:f},{token:"string",start:"'",end:"'"},{token:"constant.numeric",regex:a},{token:"constant.numeric",regex:t+"\\b"},{token:e,regex:"[a-zA-Z_][a-zA-Z0-9_]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!=|[%&|`]"},{token:"punctuation.operator",regex:";"},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]]"},{token:"paren.rparen",regex:"[\\)\\}]",next:"pop"}],variables:[{token:"variable",regex:/(\$)(\w+)/},{token:["variable","paren.lparen"],regex:/(\$)(\()/,push:"start"},{token:["variable","paren.lparen","keyword.operator","variable","keyword.operator"],regex:/(\$)(\{)([#!]?)(\w+|[*@#?\-$!0_])(:[?+\-=]?|##?|%%?|,,?\/|\^\^?)?/,push:"start"},{token:"variable",regex:/\$[*@#?\-$!0_]/},{token:["variable","paren.lparen"],regex:/(\$)(\{)/,push:"start"}]},this.normalizeRules()};r.inherits(u,i),t.ShHighlightRules=u}),define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),define("ace/mode/sh",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sh_highlight_rules","ace/range","ace/mode/folding/cstyle","ace/mode/behaviour/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sh_highlight_rules").ShHighlightRules,o=e("../range").Range,u=e("./folding/cstyle").FoldMode,a=e("./behaviour/cstyle").CstyleBehaviour,f=function(){this.HighlightRules=s,this.foldingRules=new u,this.$behaviour=new a};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[:]\s*$/);o&&(r+=n)}return r};var e={pass:1,"return":1,raise:1,"break":1,"continue":1};this.checkOutdent=function(t,n,r){if(r!=="\r\n"&&r!=="\r"&&r!=="\n")return!1;var i=this.getTokenizer().getLineTokens(n.trim(),t).tokens;if(!i)return!1;do var s=i.pop();while(s&&(s.type=="comment"||s.type=="text"&&s.value.match(/^\s+$/)));return s?s.type=="keyword"&&e[s.value]:!1},this.autoOutdent=function(e,t,n){n+=1;var r=this.$getIndent(t.getLine(n)),i=t.getTabString();r.slice(-i.length)==i&&t.remove(new o(n,r.length-i.length,n,r.length))},this.$id="ace/mode/sh"}.call(f.prototype),t.Mode=f}); (function() { 2 | window.require(["ace/mode/sh"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /public/vendor/js/popper.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2019 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); 5 | //# sourceMappingURL=popper.min.js.map 6 | -------------------------------------------------------------------------------- /public/vendor/js/theme-chrome.js: -------------------------------------------------------------------------------- 1 | define("ace/theme/chrome",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-chrome",t.cssText='.ace-chrome .ace_gutter {background: #ebebeb;color: #333;overflow : hidden;}.ace-chrome .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-chrome {background-color: #FFFFFF;color: black;}.ace-chrome .ace_cursor {color: black;}.ace-chrome .ace_invisible {color: rgb(191, 191, 191);}.ace-chrome .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-chrome .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-chrome .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-chrome .ace_invalid {background-color: rgb(153, 0, 0);color: white;}.ace-chrome .ace_fold {}.ace-chrome .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-chrome .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-chrome .ace_support.ace_type,.ace-chrome .ace_support.ace_class.ace-chrome .ace_support.ace_other {color: rgb(109, 121, 222);}.ace-chrome .ace_variable.ace_parameter {font-style:italic;color:#FD971F;}.ace-chrome .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-chrome .ace_comment {color: #236e24;}.ace-chrome .ace_comment.ace_doc {color: #236e24;}.ace-chrome .ace_comment.ace_doc.ace_tag {color: #236e24;}.ace-chrome .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-chrome .ace_variable {color: rgb(49, 132, 149);}.ace-chrome .ace_xml-pe {color: rgb(104, 104, 91);}.ace-chrome .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-chrome .ace_heading {color: rgb(12, 7, 255);}.ace-chrome .ace_list {color:rgb(185, 6, 144);}.ace-chrome .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-chrome .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-chrome .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-chrome .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-chrome .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-chrome .ace_gutter-active-line {background-color : #dcdcdc;}.ace-chrome .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-chrome .ace_storage,.ace-chrome .ace_keyword,.ace-chrome .ace_meta.ace_tag {color: rgb(147, 15, 128);}.ace-chrome .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-chrome .ace_string {color: #1A1AA6;}.ace-chrome .ace_entity.ace_other.ace_attribute-name {color: #994409;}.ace-chrome .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); (function() { 2 | window.require(["ace/theme/chrome"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:recommended" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /root/defaults/default: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | location / { 4 | root /assets; 5 | autoindex on; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /root/defaults/nginx.conf: -------------------------------------------------------------------------------- 1 | user nbxyz; 2 | worker_processes 4; 3 | pid /run/nginx.pid; 4 | include /etc/nginx/modules/*.conf; 5 | 6 | events { 7 | worker_connections 768; 8 | } 9 | 10 | http { 11 | sendfile on; 12 | tcp_nopush on; 13 | tcp_nodelay on; 14 | keepalive_timeout 65; 15 | types_hash_max_size 2048; 16 | client_max_body_size 0; 17 | include /etc/nginx/mime.types; 18 | default_type application/octet-stream; 19 | access_log /config/log/nginx/access.log; 20 | error_log /config/log/nginx/error.log; 21 | gzip on; 22 | gzip_disable "msie6"; 23 | include /config/nginx/site-confs/*; 24 | 25 | } 26 | daemon off; 27 | -------------------------------------------------------------------------------- /root/donate.txt: -------------------------------------------------------------------------------- 1 | opencollective: https://opencollective.com/netbootxyz/donate 2 | github: https://github.com/sponsors/netbootxyz 3 | -------------------------------------------------------------------------------- /root/etc/supervisor.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | 5 | [program:syslog-ng] 6 | command=/usr/sbin/syslog-ng --foreground --no-caps 7 | stdout_syslog=true 8 | stdout_capture_maxbytes=1MB 9 | priority = 1 10 | 11 | [program:nginx] 12 | command = /usr/sbin/nginx -c /config/nginx/nginx.conf 13 | startretries = 2 14 | daemon=off 15 | priority = 2 16 | 17 | [program:webapp] 18 | environment=NODE_ENV="production",PORT=3000 19 | command=/usr/bin/node app.js 20 | user=nbxyz 21 | directory=/app 22 | priority = 3 23 | 24 | [program:in.tftpd] 25 | command=/usr/sbin/in.tftpd -Lvvv --user nbxyz --secure %(ENV_TFTPD_OPTS)s /config/menus 26 | stdout_logfile=/config/tftpd.log 27 | redirect_stderr=true 28 | priority = 4 29 | 30 | [program:messages-log] 31 | command=tail -f /var/log/messages 32 | stdout_logfile=/dev/stdout 33 | stdout_logfile_maxbytes=0 34 | -------------------------------------------------------------------------------- /root/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # make our folders 4 | mkdir -p \ 5 | /assets \ 6 | /config/nginx/site-confs \ 7 | /config/log/nginx \ 8 | /run \ 9 | /var/lib/nginx/tmp/client_body \ 10 | /var/tmp/nginx 11 | 12 | # copy config files 13 | [[ ! -f /config/nginx/nginx.conf ]] && \ 14 | cp /defaults/nginx.conf /config/nginx/nginx.conf 15 | [[ ! -f /config/nginx/site-confs/default ]] && \ 16 | cp /defaults/default /config/nginx/site-confs/default 17 | 18 | # Ownership 19 | chown -R nbxyz:nbxyz /assets 20 | chown -R nbxyz:nbxyz /var/lib/nginx 21 | chown -R nbxyz:nbxyz /var/log/nginx 22 | 23 | # create local logs dir 24 | mkdir -p \ 25 | /config/menus/remote \ 26 | /config/menus/local 27 | 28 | # download menus if not found 29 | if [[ ! -f /config/menus/remote/menu.ipxe ]]; then 30 | if [[ -z ${MENU_VERSION+x} ]]; then \ 31 | MENU_VERSION=$(curl -sL "https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest" | jq -r '.tag_name') 32 | fi 33 | echo "[netbootxyz-init] Downloading netboot.xyz at ${MENU_VERSION}" 34 | # menu files 35 | curl -o \ 36 | /config/endpoints.yml -sL \ 37 | "https://raw.githubusercontent.com/netbootxyz/netboot.xyz/${MENU_VERSION}/endpoints.yml" 38 | curl -o \ 39 | /tmp/menus.tar.gz -sL \ 40 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/menus.tar.gz" 41 | tar xf \ 42 | /tmp/menus.tar.gz -C \ 43 | /config/menus/remote 44 | # boot files 45 | curl -o \ 46 | /config/menus/remote/netboot.xyz.kpxe -sL \ 47 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz.kpxe" 48 | curl -o \ 49 | /config/menus/remote/netboot.xyz-undionly.kpxe -sL \ 50 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-undionly.kpxe" 51 | curl -o \ 52 | /config/menus/remote/netboot.xyz.efi -sL \ 53 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz.efi" 54 | curl -o \ 55 | /config/menus/remote/netboot.xyz-snp.efi -sL \ 56 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-snp.efi" 57 | curl -o \ 58 | /config/menus/remote/netboot.xyz-snponly.efi -sL \ 59 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-snponly.efi" 60 | curl -o \ 61 | /config/menus/remote/netboot.xyz-arm64.efi -sL \ 62 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64.efi" 63 | curl -o \ 64 | /config/menus/remote/netboot.xyz-arm64-snp.efi -sL \ 65 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64-snp.efi" 66 | curl -o \ 67 | /config/menus/remote/netboot.xyz-arm64-snponly.efi -sL \ 68 | "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64-snponly.efi" 69 | # layer and cleanup 70 | echo -n ${MENU_VERSION} > /config/menuversion.txt 71 | cp -r /config/menus/remote/* /config/menus 72 | rm -f /tmp/menus.tar.gz 73 | fi 74 | 75 | # Ownership 76 | chown -R nbxyz:nbxyz /config 77 | 78 | echo " _ _ _ " 79 | echo " _ __ ___| |_| |__ ___ ___ | |_ __ ___ _ ____ " 80 | echo "| '_ \ / _ \ __| '_ \ / _ \ / _ \| __| \ \/ / | | |_ / " 81 | echo "| | | | __/ |_| |_) | (_) | (_) | |_ _ > <| |_| |/ / " 82 | echo "|_| |_|\___|\__|_.__/ \___/ \___/ \__(_)_/\_\\__, /___| " 83 | echo " |___/ " 84 | 85 | supervisord -c /etc/supervisor.conf 86 | --------------------------------------------------------------------------------