mirror of
https://github.com/kopia/kopia.git
synced 2026-10-21 13:44:48 -04:00
kopia-ui: misc
- launch server on a randomized port - added support for connecting to remote server - embedded version info in the app and htmlui
This commit is contained in:
1 parent
c5cf95fdf6
commit
e6fe1aa7f3
10 files changed
+135
-53
No files matched your search
@@ -26,7 +26,7 @@ let tray = null
|
||||
let configWindow = null;
|
||||
let mainWindow = null;
|
||||
|
||||
function advancedConfiguration() {
|
||||
function serverConfiguration() {
|
||||
if (configWindow) {
|
||||
configWindow.focus();
|
||||
return;
|
||||
@@ -42,7 +42,7 @@ function advancedConfiguration() {
|
||||
})
|
||||
|
||||
if (isDev) {
|
||||
configWindow.loadURL('http://localhost:3000');
|
||||
configWindow.loadURL('http://localhost:3000/?ts=' + new Date().valueOf());
|
||||
} else {
|
||||
configWindow.loadFile('./build/index.html');
|
||||
}
|
||||
@@ -215,7 +215,7 @@ function updateTrayContextMenu() {
|
||||
console.log('updating tray...');
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{ label: 'Show Main Window', click: showMainWindow },
|
||||
{ label: 'Advanced Configuration...', click: advancedConfiguration },
|
||||
{ label: 'Server Configuration...', click: serverConfiguration },
|
||||
{ label: 'Check For Updates Now', click: checkForUpdates },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Launch At Startup', type: 'checkbox', click: toggleLaunchAtStartup, checked: willLaunchAtStartup() },
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Kopia - Advanced Configuration</title>
|
||||
<title>Kopia v%REACT_APP_SHORT_VERSION_INFO% - Server Configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<hr/>
|
||||
<p class="version-info">Version %REACT_APP_FULL_VERSION_INFO%</p>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
+52
-25
@@ -5,10 +5,10 @@ const { appendToLog } = require('./logging');
|
||||
const { defaultServerBinary } = require('./utils');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
let serverProcess = null;
|
||||
let serverCertSHA256 = "";
|
||||
let serverPassword = "";
|
||||
let serverAddress = "";
|
||||
let runningServerProcess = null;
|
||||
let runningServerCertSHA256 = "";
|
||||
let runningServerPassword = "";
|
||||
let runningServerAddress = "";
|
||||
|
||||
function detectServerParam(data) {
|
||||
let lines = (data + '').split('\n');
|
||||
@@ -22,15 +22,15 @@ function detectServerParam(data) {
|
||||
const value = lines[i].substring(p + 2);
|
||||
switch (key) {
|
||||
case "SERVER PASSWORD":
|
||||
serverPassword = value;
|
||||
runningServerPassword = value;
|
||||
break;
|
||||
|
||||
case "SERVER CERT SHA256":
|
||||
serverCertSHA256 = value;
|
||||
runningServerCertSHA256 = value;
|
||||
break;
|
||||
|
||||
case "SERVER ADDRESS":
|
||||
serverAddress = value;
|
||||
runningServerAddress = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -55,30 +55,31 @@ function startServer() {
|
||||
|
||||
args.push('--random-password')
|
||||
args.push('--tls-generate-cert')
|
||||
args.push('--address=localhost:0')
|
||||
// args.push('--auto-shutdown=600s')
|
||||
|
||||
console.log(`spawning ${kopiaPath} ${args.join(' ')}`);
|
||||
serverProcess = spawn(kopiaPath, args);
|
||||
runningServerProcess = spawn(kopiaPath, args);
|
||||
ipcMain.emit('server-status-updated');
|
||||
|
||||
serverProcess.stdout.on('data', appendToLog);
|
||||
serverProcess.stderr.on('data', detectServerParam);
|
||||
runningServerProcess.stdout.on('data', appendToLog);
|
||||
runningServerProcess.stderr.on('data', detectServerParam);
|
||||
|
||||
serverProcess.on('close', (code, signal) => {
|
||||
runningServerProcess.on('close', (code, signal) => {
|
||||
appendToLog(`child process exited with code ${code} and signal ${signal}`);
|
||||
serverProcess = null;
|
||||
runningServerProcess = null;
|
||||
ipcMain.emit('server-status-updated');
|
||||
});
|
||||
}
|
||||
|
||||
function stopServer() {
|
||||
if (!serverProcess) {
|
||||
if (!runningServerProcess) {
|
||||
console.log('stopServer: server not started');
|
||||
return;
|
||||
}
|
||||
|
||||
serverProcess.kill();
|
||||
serverProcess = null;
|
||||
runningServerProcess.kill();
|
||||
runningServerProcess = null;
|
||||
}
|
||||
|
||||
ipcMain.on('subscribe-to-status', (event, arg) => {
|
||||
@@ -90,21 +91,47 @@ ipcMain.on('subscribe-to-status', (event, arg) => {
|
||||
});
|
||||
|
||||
function getServerStatus() {
|
||||
if (!serverProcess) {
|
||||
return "Stopped";
|
||||
}
|
||||
if (config.get('remoteServer')) {
|
||||
return "Remote";
|
||||
} else {
|
||||
if (!runningServerProcess) {
|
||||
return "Stopped";
|
||||
}
|
||||
|
||||
return "Running";
|
||||
return "Running";
|
||||
}
|
||||
};
|
||||
|
||||
function getServerAddress() {
|
||||
return "localhost:51515";
|
||||
function getActiveServerAddress() {
|
||||
if (config.get('remoteServer')) {
|
||||
return config.get('remoteServerAddress');
|
||||
} else {
|
||||
return runningServerAddress;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function getActiveServerCertSHA256() {
|
||||
if (config.get('remoteServer')) {
|
||||
return config.get('remoteServerCertificateSHA256');
|
||||
} else {
|
||||
return runningServerCertSHA256;
|
||||
}
|
||||
};
|
||||
|
||||
function getActiveServerPassword() {
|
||||
if (config.get('remoteServer')) {
|
||||
return config.get('remoteServerPassword');
|
||||
} else {
|
||||
return runningServerPassword;
|
||||
}
|
||||
};
|
||||
|
||||
function sendStatusUpdate(sender) {
|
||||
sender.send('status-updated', {
|
||||
status: getServerStatus(),
|
||||
serverAddress: getServerAddress(),
|
||||
serverAddress: getActiveServerAddress() || "<pending>",
|
||||
serverCertSHA256: getActiveServerCertSHA256() || "<pending>",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -117,15 +144,15 @@ module.exports = {
|
||||
},
|
||||
|
||||
getServerAddress() {
|
||||
return serverAddress;
|
||||
return getActiveServerAddress();
|
||||
},
|
||||
|
||||
getServerCertSHA256() {
|
||||
return serverCertSHA256;
|
||||
return getActiveServerCertSHA256();
|
||||
},
|
||||
|
||||
getServerPassword() {
|
||||
return serverPassword;
|
||||
return getActiveServerPassword();
|
||||
},
|
||||
|
||||
stopServer: stopServer,
|
||||
|
||||
@@ -34,4 +34,10 @@ div.tab-body {
|
||||
font-weight: bold;
|
||||
color: #000080;
|
||||
font-family: Consolas, 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.version-info {
|
||||
padding-left: 10px;
|
||||
color: #aaa;
|
||||
font-size: 80%;
|
||||
}
|
||||
+2
-1
@@ -18,7 +18,6 @@ function App() {
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<span className="title">Kopia</span>
|
||||
</header>
|
||||
<ServerStatus />
|
||||
<hr/>
|
||||
<Tabs defaultActiveKey="config" transition={false}>
|
||||
<Tab eventKey="config" title="Server Configuration">
|
||||
@@ -32,6 +31,8 @@ function App() {
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<hr/>
|
||||
<ServerStatus />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
+38
-21
@@ -10,14 +10,16 @@ export default class ServerConfig extends Component {
|
||||
"remoteServer": false,
|
||||
"kopiaPath": "",
|
||||
"configFile": "",
|
||||
"repositoryPassword": "",
|
||||
'serverUsername': 'kopia',
|
||||
"serverAddress": "https://localhost:51515",
|
||||
"serverPassword": "",
|
||||
|
||||
"remoteServerAddress": "",
|
||||
"remoteServerPassword": "",
|
||||
"remoteServerCertificateSHA256": "",
|
||||
};
|
||||
this.updateState = this.updateState.bind(this);
|
||||
this.saveConfiguration = this.saveConfiguration.bind(this);
|
||||
|
||||
this.validateRemoteServerAddress = this.validateRemoteServerAddress.bind(this);
|
||||
|
||||
if (window.require) {
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
|
||||
@@ -50,6 +52,24 @@ export default class ServerConfig extends Component {
|
||||
window.close();
|
||||
}
|
||||
|
||||
validateRemoteServerAddress() {
|
||||
return false;
|
||||
}
|
||||
|
||||
isValidHttpsURL(v) {
|
||||
try {
|
||||
const url = new URL(v);
|
||||
return url.protocol === "https:";
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
isValidSHA256(v) {
|
||||
const re = /^[0-9A-Fa-f]{64}$/g;
|
||||
return re.test(v);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<Form>
|
||||
@@ -70,34 +90,31 @@ export default class ServerConfig extends Component {
|
||||
<Form.Control type="text" placeholder="Configuration file" value={this.state.configFile} onChange={this.updateState} />
|
||||
<Form.Text className="text-muted">Uses default configuration path, if not set.</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="repositoryPassword">
|
||||
<Form.Label>Repository Password</Form.Label>
|
||||
<Form.Control type="password" placeholder="Password" value={this.state.repositoryPassword} onChange={this.updateState} />
|
||||
<Form.Text className="text-muted">Uses saved password if not set.</Form.Text>
|
||||
</Form.Group>
|
||||
</React.Fragment>
|
||||
</div>}
|
||||
<Form.Group controlId="connectToServer">
|
||||
<Form.Check type="radio" name="serverType" label="Connect To Server" checked={this.state.remoteServer} onChange={() => this.setState({ 'remoteServer': true })} />
|
||||
<Form.Check type="radio" name="serverType" label="Connect To Remote Server" checked={this.state.remoteServer} onChange={() => this.setState({ 'remoteServer': true })} />
|
||||
</Form.Group>
|
||||
{this.state.remoteServer &&
|
||||
<div className="indented">
|
||||
<React.Fragment>
|
||||
<Form.Group controlId="serverAddress">
|
||||
<Form.Group controlId="remoteServerAddress">
|
||||
<Form.Label>Server Address</Form.Label>
|
||||
<Form.Control type="text" placeholder="Enter address" value={this.state.serverAddress} onChange={this.updateState} />
|
||||
<Form.Text className="text-muted">To connect to local server, use localhost:51515</Form.Text>
|
||||
<Form.Control type="text" isInvalid={!this.isValidHttpsURL(this.state.remoteServerAddress)} placeholder="Enter address" value={this.state.remoteServerAddress} onChange={this.updateState} />
|
||||
<Form.Text className="text-muted">Enter server URL https://server:port</Form.Text>
|
||||
<Form.Control.Feedback type="invalid">Must be valid server url starting with https://</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="serverUsername">
|
||||
<Form.Label>Server Username</Form.Label>
|
||||
<Form.Control type="text" placeholder="Enter username" value={this.state.serverUsername} onChange={this.updateState} />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="serverPassword">
|
||||
<Form.Group controlId="remoteServerPassword">
|
||||
<Form.Label>Server Password</Form.Label>
|
||||
<Form.Control type="password" placeholder="Password" value={this.state.serverPassword} onChange={this.updateState} />
|
||||
<Form.Control type="password" isInvalid={!this.state.remoteServerPassword} placeholder="Password" value={this.state.remoteServerPassword} onChange={this.updateState} />
|
||||
<Form.Control.Feedback type="invalid">Password cannot be empty.</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="remoteServerCertificateSHA256">
|
||||
<Form.Label>Server Certificate Fingerprint</Form.Label>
|
||||
<Form.Control type="text" isInvalid={!this.isValidSHA256(this.state.remoteServerCertificateSHA256)} placeholder="server certificate fingerprint (SHA256)" value={this.state.remoteServerCertificateSHA256} onChange={this.updateState} />
|
||||
<Form.Control.Feedback type="invalid">Must be valid server fingerprint</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
</React.Fragment>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,6 @@ export default class ServerStatus extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>Server: <b>{this.state.status}</b> on <code>{this.state.serverAddress}</code></div>;
|
||||
return <div>Server: <code>{this.state.status}</code><br/>URL: <code>{this.state.serverAddress}</code><br/>TLS Certificate Fingerprint: <code>{this.state.serverCertSHA256}</code></div>;
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,13 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Kopia UI</title>
|
||||
<title>Kopia UI v%REACT_APP_SHORT_VERSION_INFO%</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<hr/>
|
||||
<p class="version-info">Version %REACT_APP_FULL_VERSION_INFO%</p>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
@@ -53,4 +53,10 @@ div.tab-body {
|
||||
|
||||
#snapshot-path {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.version-info {
|
||||
padding-left: 10px;
|
||||
color: #aaa;
|
||||
font-size: 80%;
|
||||
}
|
||||
@@ -140,6 +140,27 @@ else
|
||||
curl -LsS https://github.com/goreleaser/goreleaser/releases/download/$(GORELEASER_VERSION)/goreleaser_$$(uname -s)_$$(uname -m).tar.gz | tar zx -C $(TOOLS_DIR)/goreleaser-$(GORELEASER_VERSION)
|
||||
endif
|
||||
|
||||
ifeq ($(TRAVIS_PULL_REQUEST),false)
|
||||
|
||||
ifneq ($(TRAVIS_TAG),)
|
||||
# travis, tagged release
|
||||
KOPIA_VERSION:=$(TRAVIS_TAG:v%=%)
|
||||
else
|
||||
# travis, non-tagged release
|
||||
KOPIA_VERSION:=$(shell date +%Y%m%d).0.$(TRAVIS_BUILD_NUMBER)
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
# non-travis, or travis PR
|
||||
KOPIA_VERSION:=$(shell date +%Y%m%d).0.0-$(shell git rev-parse --short HEAD)
|
||||
|
||||
endif
|
||||
|
||||
# embedded in the HTML pages
|
||||
export REACT_APP_SHORT_VERSION_INFO:=$(KOPIA_VERSION)
|
||||
export REACT_APP_FULL_VERSION_INFO:=$(KOPIA_VERSION) built on $(shell date) $(shell hostname)
|
||||
|
||||
clean-tools:
|
||||
rm -rf $(TOOLS_DIR)
|
||||
|
||||
|
||||
Reference in new issue
Block a user