diff --git a/config/database.php b/config/database.php index 63b0b9cb6..32fca7581 100644 --- a/config/database.php +++ b/config/database.php @@ -96,6 +96,8 @@ return [ 'client' => 'predis', 'default' => [ + 'scheme' => env('REDIS_SCHEME', 'tcp'), + 'path' => env('REDIS_PATH', '/run/redis/redis.sock'), 'host' => env('REDIS_HOST', 'localhost'), 'password' => env('REDIS_PASSWORD', null), 'port' => env('REDIS_PORT', 6379), @@ -103,6 +105,8 @@ return [ ], 'sessions' => [ + 'scheme' => env('REDIS_SCHEME', 'tcp'), + 'path' => env('REDIS_PATH', '/run/redis/redis.sock'), 'host' => env('REDIS_HOST', 'localhost'), 'password' => env('REDIS_PASSWORD', null), 'port' => env('REDIS_PORT', 6379), diff --git a/database/migrations/2020_04_03_230614_create_backups_table.php b/database/migrations/2020_04_03_230614_create_backups_table.php index 10d0794d9..68eeee2ce 100644 --- a/database/migrations/2020_04_03_230614_create_backups_table.php +++ b/database/migrations/2020_04_03_230614_create_backups_table.php @@ -26,7 +26,7 @@ class CreateBackupsTable extends Migration // Take any of the results, most likely "backups" and "backup_logs" and rename them to have a // suffix so data isn't completely lost, but they're no longer in the way of this migration... foreach ($results as $result) { - Schema::rename($result['TABLE_NAME'], $result['TABLE_NAME'] . '_plugin_bak'); + Schema::rename($result->TABLE_NAME, $result->TABLE_NAME. '_plugin_bak'); } Schema::create('backups', function (Blueprint $table) { diff --git a/package.json b/package.json index e939af813..15e96c4bd 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,8 @@ "@fortawesome/free-solid-svg-icons": "^5.9.0", "@fortawesome/react-fontawesome": "0.1.4", "axios": "^0.19.2", - "ayu-ace": "^2.0.4", - "brace": "^0.11.1", "chart.js": "^2.8.0", + "codemirror": "^5.57.0", "date-fns": "^2.14.0", "debounce": "^1.2.0", "deepmerge": "^4.2.2", @@ -57,6 +56,7 @@ "@babel/preset-typescript": "^7.7.4", "@babel/runtime": "^7.7.5", "@types/chart.js": "^2.8.5", + "@types/codemirror": "^0.0.98", "@types/debounce": "^1.2.0", "@types/events": "^3.0.0", "@types/node": "^12.6.9", diff --git a/public/themes/pterodactyl/js/admin/node/view-servers.js b/public/themes/pterodactyl/js/admin/node/view-servers.js deleted file mode 100644 index 96950cd19..000000000 --- a/public/themes/pterodactyl/js/admin/node/view-servers.js +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2015 - 2017 Dane Everitt -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -(function initSocket() { - if (typeof $.notifyDefaults !== 'function') { - console.error('Notify does not appear to be loaded.'); - return; - } - - if (typeof io !== 'function') { - console.error('Socket.io is reqired to use this panel.'); - return; - } - - $.notifyDefaults({ - placement: { - from: 'bottom', - align: 'right' - }, - newest_on_top: true, - delay: 2000, - animate: { - enter: 'animated zoomInDown', - exit: 'animated zoomOutDown' - } - }); - - var notifySocketError = false; - // Main Socket Object - window.Socket = io(Pterodactyl.node.scheme + '://' + Pterodactyl.node.fqdn + ':' + Pterodactyl.node.daemonListen + '/v1/stats/', { - 'query': 'token=' + Pterodactyl.node.daemonSecret, - }); - - // Socket Failed to Connect - Socket.io.on('connect_error', function (err) { - if(typeof notifySocketError !== 'object') { - notifySocketError = $.notify({ - message: 'There was an error attempting to establish a WebSocket connection to the Daemon. This panel will not work as expected.

' + err, - }, { - type: 'danger', - delay: 0 - }); - } - }); - - // Connected to Socket Successfully - Socket.on('connect', function () { - if (notifySocketError !== false) { - notifySocketError.close(); - notifySocketError = false; - } - }); - - Socket.on('error', function (err) { - console.error('There was an error while attemping to connect to the websocket: ' + err + '\n\nPlease try loading this page again.'); - }); - - Socket.on('live-stats', function (data) { - $.each(data.servers, function (uuid, info) { - var element = $('tr[data-server="' + uuid + '"]'); - switch (info.status) { - case 0: - element.find('[data-action="status"]').html('Offline'); - break; - case 1: - element.find('[data-action="status"]').html('Online'); - break; - case 2: - element.find('[data-action="status"]').html('Starting'); - break; - case 3: - element.find('[data-action="status"]').html('Stopping'); - break; - case 20: - element.find('[data-action="status"]').html('Installing'); - break; - case 30: - element.find('[data-action="status"]').html('Suspended'); - break; - } - if (info.status !== 0) { - var cpuMax = element.find('[data-action="cpu"]').data('cpumax'); - var currentCpu = info.proc.cpu.total; - if (cpuMax !== 0) { - currentCpu = parseFloat(((info.proc.cpu.total / cpuMax) * 100).toFixed(2).toString()); - } - element.find('[data-action="memory"]').html(parseInt(info.proc.memory.total / (1024 * 1024))); - element.find('[data-action="disk"]').html(parseInt(info.proc.disk.used)); - element.find('[data-action="cpu"]').html(currentCpu); - } else { - element.find('[data-action="memory"]').html('--'); - element.find('[data-action="disk"]').html('--'); - element.find('[data-action="cpu"]').html('--'); - } - }); - }); -})(); \ No newline at end of file diff --git a/resources/scripts/components/elements/AceEditor.tsx b/resources/scripts/components/elements/AceEditor.tsx deleted file mode 100644 index 47fba4edb..000000000 --- a/resources/scripts/components/elements/AceEditor.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import ace, { Editor } from 'brace'; -import styled from 'styled-components/macro'; -import tw from 'twin.macro'; -import modes from '@/modes'; - -// @ts-ignore -require('brace/ext/modelist'); -require('ayu-ace/mirage'); - -const EditorContainer = styled.div` - min-height: 16rem; - height: calc(100vh - 20rem); - ${tw`relative`}; - - #editor { - ${tw`rounded h-full`}; - } -`; - -Object.keys(modes).forEach(mode => require(`brace/mode/${mode}`)); -const modelist = ace.acequire('ace/ext/modelist'); - -export interface Props { - style?: React.CSSProperties; - initialContent?: string; - mode: string; - filename?: string; - onModeChanged: (mode: string) => void; - fetchContent: (callback: () => Promise) => void; - onContentSaved: () => void; -} - -export default ({ style, initialContent, filename, mode, fetchContent, onContentSaved, onModeChanged }: Props) => { - const [ editor, setEditor ] = useState(); - const ref = useCallback(node => { - if (node) setEditor(ace.edit('editor')); - }, []); - - useEffect(() => { - if (modelist && filename) { - onModeChanged(modelist.getModeForPath(filename).mode.replace(/^ace\/mode\//, '')); - } - }, [ filename ]); - - useEffect(() => { - editor && editor.session.setMode(`ace/mode/${mode}`); - }, [ editor, mode ]); - - useEffect(() => { - editor && editor.session.setValue(initialContent || ''); - }, [ editor, initialContent ]); - - useEffect(() => { - if (!editor) { - fetchContent(() => Promise.reject(new Error('no editor session has been configured'))); - return; - } - - editor.setTheme('ace/theme/ayu-mirage'); - - editor.$blockScrolling = Infinity; - editor.container.style.lineHeight = '1.375rem'; - editor.container.style.fontWeight = '500'; - editor.renderer.updateFontSize(); - editor.renderer.setShowPrintMargin(false); - editor.session.setTabSize(4); - editor.session.setUseSoftTabs(true); - - editor.commands.addCommand({ - name: 'Save', - bindKey: { win: 'Ctrl-s', mac: 'Command-s' }, - exec: () => onContentSaved(), - }); - - fetchContent(() => Promise.resolve(editor.session.getValue())); - }, [ editor, fetchContent, onContentSaved ]); - - return ( - -
- - ); -}; diff --git a/resources/scripts/components/elements/CodemirrorEditor.tsx b/resources/scripts/components/elements/CodemirrorEditor.tsx new file mode 100644 index 000000000..50b5449c5 --- /dev/null +++ b/resources/scripts/components/elements/CodemirrorEditor.tsx @@ -0,0 +1,217 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import CodeMirror from 'codemirror'; +import styled from 'styled-components/macro'; +import tw from 'twin.macro'; +import modes, { Mode } from '@/modes'; + +require('codemirror/lib/codemirror.css'); +require('codemirror/theme/ayu-mirage.css'); +require('codemirror/addon/edit/closebrackets'); +require('codemirror/addon/edit/closetag'); +require('codemirror/addon/edit/matchbrackets'); +require('codemirror/addon/edit/matchtags'); +require('codemirror/addon/edit/trailingspace'); +require('codemirror/addon/fold/foldcode'); +require('codemirror/addon/fold/foldgutter.css'); +require('codemirror/addon/fold/foldgutter'); +require('codemirror/addon/fold/brace-fold'); +require('codemirror/addon/fold/comment-fold'); +require('codemirror/addon/fold/indent-fold'); +require('codemirror/addon/fold/markdown-fold'); +require('codemirror/addon/fold/xml-fold'); +require('codemirror/addon/hint/css-hint'); +require('codemirror/addon/hint/html-hint'); +require('codemirror/addon/hint/javascript-hint'); +require('codemirror/addon/hint/show-hint.css'); +require('codemirror/addon/hint/show-hint'); +require('codemirror/addon/hint/sql-hint'); +require('codemirror/addon/hint/xml-hint'); +require('codemirror/addon/mode/simple'); +require('codemirror/addon/dialog/dialog.css'); +require('codemirror/addon/dialog/dialog'); +require('codemirror/addon/scroll/annotatescrollbar'); +require('codemirror/addon/scroll/scrollpastend'); +require('codemirror/addon/scroll/simplescrollbars.css'); +require('codemirror/addon/scroll/simplescrollbars'); +require('codemirror/addon/search/jump-to-line'); +require('codemirror/addon/search/match-highlighter'); +require('codemirror/addon/search/matchesonscrollbar.css'); +require('codemirror/addon/search/matchesonscrollbar'); +require('codemirror/addon/search/search'); +require('codemirror/addon/search/searchcursor'); + +require('codemirror/mode/brainfuck/brainfuck'); +require('codemirror/mode/clike/clike'); +require('codemirror/mode/css/css'); +require('codemirror/mode/dart/dart'); +require('codemirror/mode/diff/diff'); +require('codemirror/mode/dockerfile/dockerfile'); +require('codemirror/mode/erlang/erlang'); +require('codemirror/mode/gfm/gfm'); +require('codemirror/mode/go/go'); +require('codemirror/mode/handlebars/handlebars'); +require('codemirror/mode/htmlembedded/htmlembedded'); +require('codemirror/mode/htmlmixed/htmlmixed'); +require('codemirror/mode/http/http'); +require('codemirror/mode/javascript/javascript'); +require('codemirror/mode/jsx/jsx'); +require('codemirror/mode/julia/julia'); +require('codemirror/mode/lua/lua'); +require('codemirror/mode/markdown/markdown'); +require('codemirror/mode/nginx/nginx'); +require('codemirror/mode/perl/perl'); +require('codemirror/mode/php/php'); +require('codemirror/mode/properties/properties'); +require('codemirror/mode/protobuf/protobuf'); +require('codemirror/mode/pug/pug'); +require('codemirror/mode/python/python'); +require('codemirror/mode/rpm/rpm'); +require('codemirror/mode/ruby/ruby'); +require('codemirror/mode/rust/rust'); +require('codemirror/mode/sass/sass'); +require('codemirror/mode/shell/shell'); +require('codemirror/mode/smarty/smarty'); +require('codemirror/mode/sql/sql'); +require('codemirror/mode/swift/swift'); +require('codemirror/mode/toml/toml'); +require('codemirror/mode/twig/twig'); +require('codemirror/mode/vue/vue'); +require('codemirror/mode/xml/xml'); +require('codemirror/mode/yaml/yaml'); + +const EditorContainer = styled.div` + min-height: 16rem; + height: calc(100vh - 20rem); + ${tw`relative`}; + + > div { + ${tw`rounded h-full`}; + } + + .CodeMirror { + font-size: 12px; + line-height: 1.375rem; + } + + .CodeMirror-linenumber { + padding: 1px 12px 0 12px !important; + } + + .CodeMirror-foldmarker { + color: #CBCCC6; + text-shadow: none; + margin-left: 0.25rem; + margin-right: 0.25rem; + } +`; + +export interface Props { + style?: React.CSSProperties; + initialContent?: string; + mode: string; + filename?: string; + onModeChanged: (mode: string) => void; + fetchContent: (callback: () => Promise) => void; + onContentSaved: () => void; +} + +const findModeByFilename = (filename: string) => { + for (let i = 0; i < modes.length; i++) { + const info = modes[i]; + + if (info.file && info.file.test(filename)) { + return info; + } + } + + const dot = filename.lastIndexOf('.'); + const ext = dot > -1 && filename.substring(dot + 1, filename.length); + + if (ext) { + for (let i = 0; i < modes.length; i++) { + const info = modes[i]; + if (info.ext) { + for (let j = 0; j < info.ext.length; j++) { + if (info.ext[j] === ext) { + return info; + } + } + } + } + } + + return undefined; +}; + +export default ({ style, initialContent, filename, mode, fetchContent, onContentSaved, onModeChanged }: Props) => { + const [ editor, setEditor ] = useState(); + + const ref = useCallback((node) => { + if (!node) return; + + const e = CodeMirror.fromTextArea(node, { + mode: 'text/plain', + theme: 'ayu-mirage', + indentUnit: 4, + smartIndent: true, + tabSize: 4, + indentWithTabs: false, + lineWrapping: true, + lineNumbers: true, + foldGutter: true, + fixedGutter: true, + scrollbarStyle: 'overlay', + coverGutterNextToScrollbar: false, + readOnly: false, + showCursorWhenSelecting: false, + autofocus: false, + spellcheck: true, + autocorrect: false, + autocapitalize: false, + lint: false, + // This property is actually used, the d.ts file for CodeMirror is incorrect. + // @ts-ignore + autoCloseBrackets: true, + matchBrackets: true, + gutters: [ 'CodeMirror-linenumbers', 'CodeMirror-foldgutter' ], + }); + + setEditor(e); + }, []); + + useEffect(() => { + if (filename === undefined) { + return; + } + + onModeChanged(findModeByFilename(filename)?.mime || 'text/plain'); + }, [ filename ]); + + useEffect(() => { + editor && editor.setOption('mode', mode); + }, [ editor, mode ]); + + useEffect(() => { + editor && editor.setValue(initialContent || ''); + }, [ editor, initialContent ]); + + useEffect(() => { + if (!editor) { + fetchContent(() => Promise.reject(new Error('no editor session has been configured'))); + return; + } + + editor.addKeyMap({ + 'Ctrl-S': () => onContentSaved(), + 'Cmd-S': () => onContentSaved(), + }); + + fetchContent(() => Promise.resolve(editor.getValue())); + }, [ editor, fetchContent, onContentSaved ]); + + return ( + +