├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── app-js ├── app.js ├── components │ ├── About.js │ ├── App.js │ ├── ConnectForm.js │ ├── Dashboard.js │ ├── ExternalLink.js │ ├── ServerDetails.js │ ├── ServerList.js │ ├── Storage.js │ └── SubscriptionForm.js └── utils │ └── PleskUtils.js ├── build ├── icon.icns └── icon.ico ├── css └── app.css ├── docs ├── .keep ├── _config.yml ├── assets │ └── css │ │ └── style.scss ├── images │ └── shot.png └── index.md ├── index.html ├── js └── .keep ├── main.js └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | /js/bundle.js 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 1999-2017. Parallels IP Holdings GmbH. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Plesk Desktop 2 | 3 | Desktop app for Plesk servers management. 4 | 5 | Additional details: https://plesk.github.io/desktop/ 6 | 7 | # Development Environment 8 | 9 | Installation instructions: 10 | * clone the repo 11 | * install Node 12 | * npm install 13 | * npm run watch (on a separate console) 14 | * npm start 15 | -------------------------------------------------------------------------------- /app-js/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | ReactDOM.render( 6 | , 7 | document.getElementById('container') 8 | ); 9 | -------------------------------------------------------------------------------- /app-js/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ExternalLink from './ExternalLink'; 3 | 4 | export default class About extends React.Component { 5 | render() { 6 | return ( 7 |
8 |

About

9 |

10 | GitHub: https://github.com/plesk/desktop
11 | Wiki: https://github.com/plesk/desktop/wiki
12 | Ticket: https://github.com/plesk/desktop/issues/new
13 |

14 |
15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app-js/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HashRouter as Router, Route, Link } from 'react-router-dom'; 3 | import Storage from './Storage'; 4 | import Dashboard from './Dashboard'; 5 | import ServerDetails from './ServerDetails'; 6 | import ServerList from './ServerList'; 7 | import ConnectForm from './ConnectForm'; 8 | import SubscriptionForm from './SubscriptionForm'; 9 | import About from "./About"; 10 | 11 | export default class App extends React.Component { 12 | render() { 13 | return ( 14 | 15 | 16 |
17 |
18 |
19 |
    20 |
  •  Dashboard
  • 21 |
22 |
Servers
23 | 24 |
25 |
    26 |
  •  About
  • 27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 |
38 |
39 |
40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app-js/components/ConnectForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import PleskUtils from '../utils/PleskUtils'; 4 | 5 | class ConnectForm extends React.Component { 6 | render() { 7 | return ( 8 |
9 |

Add a New Server

10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 | 23 |
24 |
25 | ); 26 | } 27 | 28 | handleSubmit(event) { 29 | event.preventDefault(); 30 | let host = event.target.querySelector('#host'); 31 | let pw = event.target.querySelector('#pw'); 32 | 33 | if (!host) { 34 | alert('Please define the host'); 35 | return; 36 | } 37 | 38 | PleskUtils.connectServer(host.value, 'admin', pw.value, this.context.storage, () => { 39 | const serverName = host.value; 40 | const server = this.context.storage.servers[serverName]; 41 | PleskUtils.syncServerState({ 42 | server, 43 | serverName, 44 | callback: (webspaces) => { 45 | this.context.storage.findSubscriptions(serverName, webspaces.map((webspace) => { 46 | return { 47 | domain: webspace.data[0].gen_info[0].name[0], 48 | domainId: webspace.id[0] 49 | }; 50 | })); 51 | } 52 | }); 53 | 54 | this.props.history.push('/'); 55 | }); 56 | 57 | } 58 | } 59 | 60 | ConnectForm.contextTypes = { 61 | storage: PropTypes.object, 62 | }; 63 | 64 | export default ConnectForm; 65 | -------------------------------------------------------------------------------- /app-js/components/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | class Dashboard extends React.Component { 6 | render() { 7 | return ( 8 |
9 |

Dashboard

10 |

11 | 12 | Connect a Server 13 | 14 |

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Servers{Object.keys(this.context.storage.servers).length}
23 |
24 | ); 25 | } 26 | } 27 | 28 | Dashboard.contextTypes = { 29 | storage: PropTypes.object, 30 | }; 31 | 32 | export default Dashboard; 33 | -------------------------------------------------------------------------------- /app-js/components/ExternalLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const addExternalProps = ({ href, ...props }) => { 4 | const electron = window.require && window.require('electron'); 5 | if (electron) { 6 | return { 7 | ...props, 8 | href: '#', 9 | onClick: event => { 10 | event.preventDefault(); 11 | electron.shell.openExternal(href); 12 | }, 13 | } 14 | } 15 | 16 | return { 17 | ...props, 18 | href, 19 | target: '_blank', 20 | } 21 | }; 22 | 23 | const ExternalLink = props => ( 24 | 25 | ); 26 | 27 | export default ExternalLink; 28 | -------------------------------------------------------------------------------- /app-js/components/ServerDetails.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Link } from 'react-router-dom'; 4 | import ExternalLink from './ExternalLink'; 5 | import PleskUtils from '../utils/PleskUtils'; 6 | 7 | class ServerDetails extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | this.state = { serverLoginGeneration: false }; 11 | } 12 | 13 | render() { 14 | const { serverName } = this.props.match.params; 15 | const { servers } = this.context.storage; 16 | const server = servers[serverName]; 17 | const domains = server && server.domains || []; 18 | 19 | return ( 20 |
21 |
22 |
23 |

Server {serverName}

24 |
25 |   26 | Login to Plesk UI 27 |   28 | 29 |   30 | Sync 31 |   32 | 33 |   34 | Disconnect 35 | 36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
Version{server && server.details && server.details.version}
OS{server && server.details && `${server.details.os} ${server.details.osVersion}`}
52 |
53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
Subscriptions{server && server.domains && server.domains.length || 0}
62 |
63 |
64 |
65 |
66 | 67 |   68 | Create Subscription 69 | 70 |
71 |
72 |
73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | {domains.map((domain) => { 83 | return ( 84 | 85 | 86 | 96 | 97 | ); 98 | })} 99 | 100 |
SubscriptionActions
{domain.domain} 87 | 88 |   89 | Login 90 |   91 | 92 |   93 | Remove 94 | 95 |
101 |
102 |
103 |
104 | ); 105 | } 106 | 107 | handleLogin(event) { 108 | event.preventDefault(); 109 | const serverName = this.props.match.params.serverName; 110 | const server = this.context.storage.servers[serverName]; 111 | const domainId = event.target.getAttribute('data-id'); 112 | 113 | if (!domainId) { 114 | this.setState({ serverLoginGeneration: true }); 115 | } 116 | 117 | PleskUtils.generateLoginUrl({ 118 | server, 119 | serverName, 120 | callback: (url) => { 121 | if (domainId) { 122 | url = `${url}&success_redirect_url=/admin/subscription/overview/id/${domainId}`; 123 | } else { 124 | this.setState({ serverLoginGeneration: false }); 125 | } 126 | 127 | const electron = window.require('electron'); 128 | electron.shell.openExternal(url); 129 | } 130 | }); 131 | } 132 | 133 | handleDisconnect(event) { 134 | event.preventDefault(); 135 | const {serverName} = this.props.match.params; 136 | this.context.storage.disconnectServer(serverName); 137 | this.props.history.push('/'); 138 | } 139 | 140 | handleSync(event) { 141 | event.preventDefault(); 142 | const serverName = this.props.match.params.serverName; 143 | const server = this.context.storage.servers[serverName]; 144 | PleskUtils.syncServerState({ 145 | server, 146 | serverName, 147 | callback: (webspaces) => { 148 | this.context.storage.findSubscriptions(serverName, webspaces.map((webspace) => { 149 | return { 150 | domain: webspace.data[0].gen_info[0].name[0], 151 | domainId: webspace.id[0] 152 | }; 153 | })); 154 | } 155 | }); 156 | } 157 | 158 | handleRemoveSubscription(event) { 159 | event.preventDefault(); 160 | const domain = event.target.getAttribute('data-id'); 161 | const serverName = this.props.match.params.serverName; 162 | const server = this.context.storage.servers[serverName]; 163 | 164 | PleskUtils.subscription.remove({ 165 | server, 166 | serverName, 167 | domain, 168 | callback: () => { 169 | this.context.storage.removeSubscription(serverName, domain); 170 | } 171 | }); 172 | } 173 | } 174 | 175 | ServerDetails.contextTypes = { 176 | storage: PropTypes.object, 177 | }; 178 | 179 | export default ServerDetails; 180 | -------------------------------------------------------------------------------- /app-js/components/ServerList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | class ServerList extends React.Component { 6 | render() { 7 | const { servers } = this.context.storage; 8 | 9 | return ( 10 | 24 | ); 25 | } 26 | } 27 | 28 | ServerList.contextTypes = { 29 | storage: PropTypes.object, 30 | }; 31 | 32 | export default ServerList; 33 | -------------------------------------------------------------------------------- /app-js/components/Storage.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const storage = window.require && window.require('electron-json-storage') || { 5 | set: (key, json, callback) => { 6 | localStorage.setItem(key, JSON.stringify(json)); 7 | callback(); 8 | }, 9 | get: (key, callback) => { 10 | const json = JSON.parse(localStorage.getItem(key)) || {}; 11 | callback(null, json); 12 | }, 13 | }; 14 | 15 | class Storage extends Component { 16 | constructor() { 17 | super(); 18 | 19 | this.state = { 20 | servers: {}, 21 | }; 22 | } 23 | 24 | componentDidMount() { 25 | storage.get('servers', (error, servers) => { 26 | this.setState({ servers }); 27 | }); 28 | } 29 | 30 | render() { 31 | return this.props.children; 32 | } 33 | 34 | getChildContext() { 35 | return { 36 | storage: { 37 | servers: this.state.servers, 38 | connectServer: this.connectServer.bind(this), 39 | disconnectServer: this.disconnectServer.bind(this), 40 | addSubscription: this.addSubscription.bind(this), 41 | removeSubscription: this.removeSubscription.bind(this), 42 | findSubscriptions: this.findSubscriptions.bind(this) 43 | }, 44 | }; 45 | } 46 | 47 | connectServer(host, login, password, details) { 48 | const { servers } = this.state; 49 | servers[host] = { login, password, details }; 50 | this.saveServersState(servers); 51 | } 52 | 53 | disconnectServer(host) { 54 | const { servers } = this.state; 55 | delete servers[host]; 56 | this.saveServersState(servers); 57 | } 58 | 59 | addSubscription(host, domain, domainId, login, password, ip) { 60 | const { servers } = this.state; 61 | servers[host].domains = servers[host].domains || []; 62 | servers[host].domains.push({ 63 | domainId: domainId, 64 | domain: domain, 65 | login: login, 66 | password: password, 67 | ip: ip 68 | }); 69 | this.saveServersState(servers); 70 | } 71 | 72 | removeSubscription(host, domain) { 73 | const { servers } = this.state; 74 | servers[host].domains = servers[host].domains || []; 75 | servers[host].domains = servers[host].domains.filter((item) => domain !== item.domain); 76 | this.saveServersState(servers); 77 | } 78 | 79 | findSubscriptions(host, domains) { 80 | const { servers } = this.state; 81 | servers[host].domains = []; 82 | domains.forEach(({ domain, domainId }) => { 83 | this.addSubscription(host, domain, domainId, '', '', ''); 84 | }); 85 | this.saveServersState(servers); 86 | } 87 | 88 | saveServersState(servers) { 89 | storage.set('servers', servers, (error) => { 90 | if (error) { 91 | throw error; 92 | } 93 | this.setState({ servers }); 94 | }); 95 | } 96 | } 97 | 98 | Storage.childContextTypes = { 99 | storage: PropTypes.object, 100 | }; 101 | 102 | export default Storage; 103 | -------------------------------------------------------------------------------- /app-js/components/SubscriptionForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import PleskUtils from '../utils/PleskUtils'; 4 | 5 | class SubscriptionForm extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { loading: false }; 9 | } 10 | 11 | render() { 12 | return ( 13 |
14 |

Add a New Subscription

15 |
16 |
17 | 18 | 19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | 27 |
28 | 35 |
36 |
37 | ); 38 | } 39 | 40 | handleSubmit(event) { 41 | event.preventDefault(); 42 | 43 | const domain = event.target.querySelector('#domain').value; 44 | const domainLogin = event.target.querySelector('#login').value; 45 | const password = event.target.querySelector('#pw').value; 46 | const serverName = this.props.match.params.serverName; 47 | const server = this.context.storage.servers[serverName]; 48 | 49 | this.setState({ loading: true }); 50 | 51 | PleskUtils.subscription.create({ 52 | server, 53 | serverName, 54 | domain, 55 | domainLogin, 56 | password, 57 | callback: (error, result) => { 58 | this.setState({ loading: false }); 59 | 60 | if (error) { 61 | console.log(error); 62 | return; 63 | } 64 | if (result.packet.system) { 65 | alert(result.packet.system[0].errtext[0]); 66 | return; 67 | } 68 | if ('error' === result.packet.webspace[0].add[0].result[0].status[0]) { 69 | alert(result.packet.webspace[0].add[0].result[0].errtext[0]); 70 | return; 71 | } 72 | const ip = !server.details.isMultiServer ? serverName : result.packet.webspace[0].add[0].result[0].ip[0]; 73 | const domainId = result.packet.webspace[0].add[0].result[0].id[0]; 74 | this.context.storage.addSubscription(serverName, domain, domainId, domainLogin, password, ip); 75 | this.props.history.push(`/server/show/${serverName}`); 76 | }, 77 | }); 78 | } 79 | } 80 | 81 | SubscriptionForm.contextTypes = { 82 | storage: PropTypes.object, 83 | }; 84 | 85 | export default SubscriptionForm; 86 | -------------------------------------------------------------------------------- /app-js/utils/PleskUtils.js: -------------------------------------------------------------------------------- 1 | import PleskApi from 'plesk-api-client'; 2 | import { parseString } from 'xml2js'; 3 | 4 | const Subscription = { 5 | _getMultiServerRequestSettings() { 6 | return ( 7 | ` 8 | 9 | plesk_rpc_forwarding_to_ext 10 | plesk-multi-server 11 | 12 | 13 | ext-plesk-multi-server:ipv4 14 | shared 15 | 16 | 17 | ext-plesk-multi-server:sync 18 | true 19 | 20 | ` 21 | ); 22 | }, 23 | 24 | _getSubscriptionCreationPacket(requestSettings, domain, domainLogin, domainPassword, ipAddress) { 25 | return ( 26 | ` 27 | ${requestSettings} 28 | 29 | 30 | 31 | ${domain} 32 | ${ipAddress} 33 | 34 | 35 | 36 | 37 | ftp_login 38 | ${domainLogin} 39 | 40 | 41 | ftp_password 42 | ${domainPassword} 43 | 44 | ${ipAddress} 45 | 46 | 47 | Default domain 48 | 49 | 50 | ` 51 | ); 52 | }, 53 | 54 | create({ domain, domainLogin, password, server, serverName, callback }) { 55 | if (!domain) { 56 | alert('Please define the domain name.'); 57 | return; 58 | } 59 | 60 | const client = new PleskApi.Client(serverName); 61 | client.setCredentials(server.login, server.password); 62 | 63 | new Promise((resolve) => { 64 | client 65 | .request('') 66 | .then((response) => { 67 | parseString(response, (error, result) => { 68 | resolve(result.packet.ip[0].get[0].result[0].addresses[0].ip_info[0].ip_address[0]); 69 | }); 70 | }); 71 | }) 72 | .then((ipAddress) => { 73 | const requestSettings = server.details.isMultiServer ? this._getMultiServerRequestSettings() : ''; 74 | const request = this._getSubscriptionCreationPacket(requestSettings, domain, domainLogin, password, ipAddress); 75 | 76 | client 77 | .request(request) 78 | .then((response) => { 79 | parseString(response, callback); 80 | }) 81 | .catch((error) => { 82 | alert(error.message); 83 | }); 84 | }); 85 | }, 86 | 87 | remove({ domain, server, serverName, callback }) { 88 | const client = new PleskApi.Client(serverName); 89 | client.setCredentials(server.login, server.password); 90 | 91 | const request = 92 | ` 93 | 94 | 95 | 96 | ${domain} 97 | 98 | 99 | 100 | `; 101 | 102 | new Promise((resolve) => { 103 | client 104 | .request(request) 105 | .then(callback) 106 | .catch((error) => { 107 | alert(error.message); 108 | }); 109 | }); 110 | } 111 | } 112 | 113 | const PleskUtils = { 114 | subscription: Subscription, 115 | 116 | connectServer(host, login, password, storage, callback) { 117 | const request = 118 | ` 119 | 120 | `; 121 | 122 | const client = new PleskApi.Client(host); 123 | client.setCredentials(login, password); 124 | client.request(request) 125 | .then((response) => { 126 | parseString(response, (error, result) => { 127 | if (error) { 128 | console.log(error); 129 | return; 130 | } 131 | 132 | if (result.packet.system) { 133 | alert(result.packet.system[0].errtext[0]); 134 | return; 135 | } 136 | 137 | const stats = result.packet.server[0].get[0].result[0].stat[0]; 138 | storage.connectServer(host, 'admin', password, { 139 | isMultiServer: false, // TODO: add detection of Plesk MultiServer instance 140 | version: stats.version[0].plesk_version[0], 141 | os: stats.version[0].plesk_os[0], 142 | osVersion: stats.version[0].plesk_os_version[0] 143 | }); 144 | 145 | callback(); 146 | }); 147 | }) 148 | .catch((error) => { 149 | alert(error.message); 150 | }); 151 | }, 152 | 153 | syncServerState({ server, serverName, callback }) { 154 | const client = new PleskApi.Client(serverName); 155 | client.setCredentials(server.login, server.password); 156 | 157 | const request = ( 158 | ` 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | ` 168 | ); 169 | 170 | client.request(request) 171 | .then((response) => { 172 | parseString(response, (error, result) => { 173 | const webspaces = result.packet.webspace[0].get[0].result; 174 | callback(webspaces); 175 | }) 176 | }); 177 | }, 178 | 179 | generateLoginUrl({ server, serverName, callback }) { 180 | const client = new PleskApi.Client(serverName); 181 | client.setCredentials(server.login, server.password); 182 | 183 | const request = ( 184 | ` 185 | 186 | 187 | ${server.login} 188 | 189 | 190 | 191 | 192 | 193 | 194 | ` 195 | ); 196 | 197 | client.request(request) 198 | .then((response) => { 199 | parseString(response, (error, result) => { 200 | const sessionId = result.packet.server[0].create_session[0].result[0].id[0]; 201 | const loginUrl = `https://${serverName}:8443/enterprise/rsession_init.php?PLESKSESSID=${sessionId}`; 202 | callback(loginUrl); 203 | }) 204 | }); 205 | } 206 | } 207 | 208 | export default PleskUtils; 209 | -------------------------------------------------------------------------------- /build/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plesk/desktop/24cb63cc6a7b458f556b5b5dbadc7d86ea2fb6dd/build/icon.icns -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plesk/desktop/24cb63cc6a7b458f556b5b5dbadc7d86ea2fb6dd/build/icon.ico -------------------------------------------------------------------------------- /css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 0px; 3 | } 4 | 5 | .sidebar { 6 | position: fixed; 7 | top: 1px; 8 | bottom: 0; 9 | left: 0; 10 | z-index: 1000; 11 | display: block; 12 | padding: 20px; 13 | overflow-x: hidden; 14 | overflow-y: auto; 15 | background-color: #f5f5f5; 16 | border-right: 1px solid #eee; 17 | } 18 | 19 | .nav-sidebar { 20 | margin-right: -21px; 21 | margin-bottom: 20px; 22 | margin-left: -20px; 23 | } 24 | .nav-sidebar > li > a { 25 | padding-right: 20px; 26 | padding-left: 20px; 27 | white-space: nowrap; 28 | } 29 | .nav-sidebar > .active > a, 30 | .nav-sidebar > .active > a:hover, 31 | .nav-sidebar > .active > a:focus { 32 | color: #fff; 33 | background-color: #428bca; 34 | } 35 | 36 | .top-buffer { 37 | margin-top:20px; 38 | } 39 | 40 | .main { 41 | padding: 20px; 42 | } 43 | @media (min-width: 768px) { 44 | .main { 45 | padding-right: 40px; 46 | padding-left: 40px; 47 | } 48 | } 49 | .main .page-header { 50 | margin-top: 0; 51 | } 52 | .sidebar .plesk-version { 53 | color: #555; 54 | } 55 | -------------------------------------------------------------------------------- /docs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plesk/desktop/24cb63cc6a7b458f556b5b5dbadc7d86ea2fb6dd/docs/.keep -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker 2 | title: Plesk Desktop 3 | description: Desktop app for Plesk servers management. 4 | -------------------------------------------------------------------------------- /docs/assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "{{ site.theme }}"; 5 | 6 | header h1:before { content: ""; } 7 | header h1 { margin: 0; } 8 | -------------------------------------------------------------------------------- /docs/images/shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plesk/desktop/24cb63cc6a7b458f556b5b5dbadc7d86ea2fb6dd/docs/images/shot.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Desktop app for Plesk servers management. 4 | 5 | ![Screenshot](/images/shot.png) 6 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plesk Desktop 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /js/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plesk/desktop/24cb63cc6a7b458f556b5b5dbadc7d86ea2fb6dd/js/.keep -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | const app = electron.app; 3 | const BrowserWindow = electron.BrowserWindow; 4 | const path = require('path'); 5 | const url = require('url'); 6 | 7 | let mainWindow; 8 | 9 | function createWindow() { 10 | mainWindow = new BrowserWindow({ width: 1020, height: 625 }); 11 | 12 | mainWindow.loadURL(url.format({ 13 | pathname: path.join(__dirname, 'index.html'), 14 | protocol: 'file:', 15 | slashes: true 16 | })); 17 | 18 | mainWindow.on('closed', () => { 19 | mainWindow = null; 20 | }); 21 | } 22 | 23 | app.commandLine.appendSwitch('ignore-certificate-errors'); 24 | 25 | app.on('ready', createWindow); 26 | 27 | app.on('window-all-closed', () => { 28 | if (process.platform !== 'darwin') { 29 | app.quit(); 30 | } 31 | }); 32 | 33 | app.on('activate', () => { 34 | if (mainWindow === null) { 35 | createWindow(); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plesk-desktop", 3 | "version": "0.0.1", 4 | "description": "Desktop app for Plesk servers management.", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "electron .", 8 | "watch": "watchify app-js/app.js -t babelify -o js/bundle.js", 9 | "dist:osx": "build --dist --platform darwin", 10 | "dist:win32": "build --dist --platform win32" 11 | }, 12 | "author": "Plesk", 13 | "license": "Apache-2.0", 14 | "repository": { 15 | "type": "git", 16 | "url": "git@github.com:plesk/desktop.git" 17 | }, 18 | "build": { 19 | "app-bundle-id": "com.plesk.plesk-desktop", 20 | "app-category-type": "public.app-category.developer-tools", 21 | "productName": "Plesk Desktop", 22 | "osx": { 23 | "title": "Plesk Desktop", 24 | "window": { 25 | "position": { 26 | "x": 250, 27 | "y": 250 28 | } 29 | } 30 | } 31 | }, 32 | "devDependencies": { 33 | "electron": "~1.6.2", 34 | "electron-builder": "^3.25.0", 35 | "electron-prebuilt": "^1.4.13", 36 | "watchify": "^3.9.0" 37 | }, 38 | "dependencies": { 39 | "babel-preset-es2015": "^6.24.0", 40 | "babel-preset-react": "^6.23.0", 41 | "babel-preset-stage-0": "^6.22.0", 42 | "babelify": "^7.3.0", 43 | "bootstrap": "^3.3.7", 44 | "electron-json-storage": "^3.0.2", 45 | "font-awesome": "^4.7.0", 46 | "plesk-api-client": "^1.0.3", 47 | "prop-types": "^15.5.8", 48 | "react": "^15.4.2", 49 | "react-dom": "^15.4.2", 50 | "react-router-dom": "^4.0.0", 51 | "xml2js": "^0.4.17" 52 | } 53 | } 54 | --------------------------------------------------------------------------------