mirror of
https://github.com/OutlineFoundation/outline-server.git
synced 2026-08-27 03:42:01 +00:00
Merge pull request #859 from Jigsaw-Code/fortuna-fix
Fix server removal
This commit is contained in:
commit
4b2cfbc573
5 changed files with 123 additions and 77 deletions
|
|
@ -80,7 +80,7 @@ describe('App', () => {
|
|||
expect(managedServers.length).toEqual(1);
|
||||
const manualServers = await manualServerRepo.listServers();
|
||||
expect(manualServers.length).toEqual(2);
|
||||
appRoot.getServerView('');
|
||||
await appRoot.getServerView('');
|
||||
const serverList = appRoot.serverList;
|
||||
|
||||
console.log(`managedServers.length: ${managedServers.length}`);
|
||||
|
|
@ -114,7 +114,8 @@ describe('App', () => {
|
|||
await app.start();
|
||||
await app.createDigitalOceanServer('fakeRegion');
|
||||
expect(appRoot.currentPage).toEqual('serverView');
|
||||
expect(appRoot.getServerView(appRoot.selectedServerId).selectedPage).toEqual('progressView');
|
||||
const view = await appRoot.getServerView(appRoot.selectedServerId);
|
||||
expect(view.selectedPage).toEqual('progressView');
|
||||
});
|
||||
|
||||
it('shows progress screen when starting with DigitalOcean servers still being created',
|
||||
|
|
@ -128,7 +129,8 @@ describe('App', () => {
|
|||
localStorage.setItem(LAST_DISPLAYED_SERVER_STORAGE_KEY, server.getId());
|
||||
await app.start();
|
||||
expect(appRoot.currentPage).toEqual('serverView');
|
||||
expect(appRoot.getServerView(appRoot.selectedServerId).selectedPage).toEqual('progressView');
|
||||
const view = await appRoot.getServerView(appRoot.selectedServerId);
|
||||
expect(view.selectedPage).toEqual('progressView');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -699,9 +699,9 @@ export class App {
|
|||
}
|
||||
|
||||
// Show the server management screen. Assumes the server is healthy.
|
||||
private setServerManagementView(server: server.Server): void {
|
||||
private async setServerManagementView(server: server.Server): Promise<void> {
|
||||
// Show view and initialize fields from selectedServer.
|
||||
const view = this.appRoot.getServerView(server.getId());
|
||||
const view = await this.appRoot.getServerView(server.getId());
|
||||
const version = server.getVersion();
|
||||
view.selectedPage = 'managementView';
|
||||
view.serverId = server.getId();
|
||||
|
|
@ -760,9 +760,9 @@ export class App {
|
|||
}, 0);
|
||||
}
|
||||
|
||||
private setServerUnreachableView(server: server.Server): void {
|
||||
private async setServerUnreachableView(server: server.Server): Promise<void> {
|
||||
// Display the unreachable server state within the server view.
|
||||
const serverView = this.appRoot.getServerView(server.getId());
|
||||
const serverView = await this.appRoot.getServerView(server.getId());
|
||||
serverView.selectedPage = 'unreachableView';
|
||||
serverView.isServerManaged = isManagedServer(server);
|
||||
serverView.serverName =
|
||||
|
|
@ -772,8 +772,8 @@ export class App {
|
|||
};
|
||||
}
|
||||
|
||||
private setServerProgressView(server: server.Server): void {
|
||||
const view = this.appRoot.getServerView(server.getId());
|
||||
private async setServerProgressView(server: server.Server): Promise<void> {
|
||||
const view = await this.appRoot.getServerView(server.getId());
|
||||
view.serverName = this.makeDisplayName(server);
|
||||
view.selectedPage = 'progressView';
|
||||
}
|
||||
|
|
@ -874,17 +874,18 @@ export class App {
|
|||
};
|
||||
}
|
||||
|
||||
private addAccessKey() {
|
||||
this.selectedServer.addAccessKey()
|
||||
.then((serverAccessKey: server.AccessKey) => {
|
||||
const uiAccessKey = this.convertToUiAccessKey(serverAccessKey);
|
||||
this.appRoot.getServerView(this.appRoot.selectedServerId).addAccessKey(uiAccessKey);
|
||||
this.appRoot.showNotification(this.appRoot.localize('notification-key-added'));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Failed to add access key: ${error}`);
|
||||
this.appRoot.showError(this.appRoot.localize('error-key-add'));
|
||||
});
|
||||
private async addAccessKey() {
|
||||
const server = this.selectedServer;
|
||||
try {
|
||||
const serverAccessKey = await server.addAccessKey();
|
||||
const uiAccessKey = this.convertToUiAccessKey(serverAccessKey);
|
||||
const serverView = await this.appRoot.getServerView(server.getId());
|
||||
serverView.addAccessKey(uiAccessKey);
|
||||
this.appRoot.showNotification(this.appRoot.localize('notification-key-added'));
|
||||
} catch (error) {
|
||||
console.error(`Failed to add access key: ${error}`);
|
||||
this.appRoot.showError(this.appRoot.localize('error-key-add'));
|
||||
}
|
||||
}
|
||||
|
||||
private renameAccessKey(accessKeyId: string, newName: string, entry: polymer.Base) {
|
||||
|
|
@ -907,7 +908,7 @@ export class App {
|
|||
if (previousLimit && limit.bytes === previousLimit.bytes) {
|
||||
return;
|
||||
}
|
||||
const serverView = this.appRoot.getServerView(this.appRoot.selectedServerId);
|
||||
const serverView = await this.appRoot.getServerView(this.appRoot.selectedServerId);
|
||||
try {
|
||||
await this.selectedServer.setDefaultDataLimit(limit);
|
||||
this.appRoot.showNotification(this.appRoot.localize('saved'));
|
||||
|
|
@ -927,7 +928,7 @@ export class App {
|
|||
}
|
||||
|
||||
private async removeDefaultDataLimit() {
|
||||
const serverView = this.appRoot.getServerView(this.appRoot.selectedServerId);
|
||||
const serverView = await this.appRoot.getServerView(this.appRoot.selectedServerId);
|
||||
const previousLimit = this.selectedServer.getDefaultDataLimit();
|
||||
try {
|
||||
await this.selectedServer.removeDefaultDataLimit();
|
||||
|
|
@ -960,7 +961,7 @@ export class App {
|
|||
Promise<boolean> {
|
||||
this.appRoot.showNotification(this.appRoot.localize('saving'));
|
||||
const server = this.idServerMap.get(serverId);
|
||||
const serverView = this.appRoot.getServerView(server.getId());
|
||||
const serverView = await this.appRoot.getServerView(server.getId());
|
||||
try {
|
||||
await server.setAccessKeyDataLimit(keyId, {bytes: dataLimitBytes});
|
||||
this.refreshTransferStats(server, serverView);
|
||||
|
|
@ -976,7 +977,7 @@ export class App {
|
|||
private async removePerKeyDataLimit(serverId: string, keyId: string): Promise<boolean> {
|
||||
this.appRoot.showNotification(this.appRoot.localize('saving'));
|
||||
const server = this.idServerMap.get(serverId);
|
||||
const serverView = this.appRoot.getServerView(server.getId());
|
||||
const serverView = await this.appRoot.getServerView(server.getId());
|
||||
try {
|
||||
await server.removeAccessKeyDataLimit(keyId);
|
||||
this.refreshTransferStats(server, serverView);
|
||||
|
|
@ -1060,16 +1061,16 @@ export class App {
|
|||
}
|
||||
}
|
||||
|
||||
private removeAccessKey(accessKeyId: string) {
|
||||
this.selectedServer.removeAccessKey(accessKeyId)
|
||||
.then(() => {
|
||||
this.appRoot.getServerView(this.appRoot.selectedServerId).removeAccessKey(accessKeyId);
|
||||
this.appRoot.showNotification(this.appRoot.localize('notification-key-removed'));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Failed to remove access key: ${error}`);
|
||||
this.appRoot.showError(this.appRoot.localize('error-key-remove'));
|
||||
});
|
||||
private async removeAccessKey(accessKeyId: string) {
|
||||
const server = this.selectedServer;
|
||||
try {
|
||||
await server.removeAccessKey(accessKeyId);
|
||||
(await this.appRoot.getServerView(server.getId())).removeAccessKey(accessKeyId);
|
||||
this.appRoot.showNotification(this.appRoot.localize('notification-key-removed'));
|
||||
} catch (error) {
|
||||
console.error(`Failed to remove access key: ${error}`);
|
||||
this.appRoot.showError(this.appRoot.localize('error-key-remove'));
|
||||
}
|
||||
}
|
||||
|
||||
private deleteServer(serverId: string) {
|
||||
|
|
@ -1124,7 +1125,7 @@ export class App {
|
|||
}
|
||||
|
||||
private async setMetricsEnabled(metricsEnabled: boolean) {
|
||||
const serverView = this.appRoot.getServerView(this.appRoot.selectedServerId);
|
||||
const serverView = await this.appRoot.getServerView(this.appRoot.selectedServerId);
|
||||
try {
|
||||
await this.selectedServer.setMetricsEnabled(metricsEnabled);
|
||||
this.appRoot.showNotification(this.appRoot.localize('saved'));
|
||||
|
|
@ -1140,7 +1141,7 @@ export class App {
|
|||
private async renameServer(newName: string) {
|
||||
const serverToRename = this.selectedServer;
|
||||
const serverId = this.appRoot.selectedServerId;
|
||||
const view = this.appRoot.getServerView(serverId);
|
||||
const view = await this.appRoot.getServerView(serverId);
|
||||
try {
|
||||
await serverToRename.setName(newName);
|
||||
view.serverName = newName;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import './outline-language-picker.js';
|
|||
import './outline-manual-server-entry.js';
|
||||
import './outline-modal-dialog.js';
|
||||
import './outline-region-picker-step';
|
||||
import './outline-server-list';
|
||||
import './outline-tos-view.js';
|
||||
|
||||
import {AppLocalizeBehavior} from '@polymer/app-localize-behavior/app-localize-behavior.js';
|
||||
|
|
@ -386,10 +387,7 @@ export class AppRoot extends mixinBehaviors
|
|||
<outline-gcp-oauth-step id="gcpOauth" localize="[[localize]]"></outline-gcp-oauth-step>
|
||||
<outline-manual-server-entry id="manualEntry" localize="[[localize]]"></outline-manual-server-entry>
|
||||
<outline-region-picker-step id="regionPicker" localize="[[localize]]"></outline-region-picker-step>
|
||||
<div id="serverView">
|
||||
<template is="dom-repeat" items="{{serverList}}" as="server">
|
||||
<outline-server-view id="serverView-{{_base64Encode(server.id)}}" language="[[language]]" localize="[[localize]]" hidden\$="{{!_isServerSelected(selectedServerId, server)}}"></outline-server-view>
|
||||
</template>
|
||||
<outline-server-list id="serverView" server-list="[[serverList]]" selected-server-id="[[selectedServerId]]" language="[[language]]" localize="[[localize]]"></outline-server-list>
|
||||
</div>
|
||||
</iron-pages>
|
||||
</div>
|
||||
|
|
@ -658,16 +656,10 @@ export class AppRoot extends mixinBehaviors
|
|||
/**
|
||||
* Gets the ServerView for the server given by its id
|
||||
* @param {string} displayServerId
|
||||
* @returns {ServerView}
|
||||
* @returns {Promise<ServerView>}
|
||||
*/
|
||||
getServerView(displayServerId) {
|
||||
if (!displayServerId) {
|
||||
return null;
|
||||
}
|
||||
// Render to ensure that the server view has been added to the DOM.
|
||||
this.$.serverView.querySelector('dom-repeat').render();
|
||||
const selectedServerId = this._base64Encode(displayServerId);
|
||||
return this.$.serverView.querySelector(`#serverView-${selectedServerId}`);
|
||||
async getServerView(displayServerId) {
|
||||
return await this.shadowRoot.querySelector('#serverView').getServerView(displayServerId);
|
||||
}
|
||||
|
||||
handleRegionSelected(/** @type {Event} */ e) {
|
||||
|
|
@ -950,12 +942,5 @@ export class AppRoot extends mixinBehaviors
|
|||
this.fire('ShowServerRequested', {displayServerId: server.id});
|
||||
this.maybeCloseDrawer();
|
||||
}
|
||||
|
||||
// Wrapper to encode a string in base64. This is necessary to set the server view IDs to
|
||||
// the display server IDs, which are URLs, so they can be used with selector methods. The IDs
|
||||
// are never decoded.
|
||||
_base64Encode(s) {
|
||||
return btoa(s).replace(/=/g, '');
|
||||
}
|
||||
}
|
||||
customElements.define(AppRoot.is, AppRoot);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2021 The Outline Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {customElement, html, LitElement, property} from 'lit-element';
|
||||
import {repeat} from 'lit-html/directives/repeat.js';
|
||||
import {ServerView} from './outline-server-view.js';
|
||||
|
||||
export interface ServerListEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
isManaged: boolean;
|
||||
isSynced: boolean;
|
||||
}
|
||||
|
||||
@customElement('outline-server-list')
|
||||
export class OutlineServerList extends LitElement {
|
||||
@property({type: Array}) serverList: ServerListEntry[];
|
||||
@property({type: String}) selectedServerId: string;
|
||||
@property({type: Function}) localize: Function;
|
||||
@property({type: String}) language: string;
|
||||
|
||||
render() {
|
||||
if (!this.serverList) {
|
||||
return;
|
||||
}
|
||||
return html`<div>${repeat(this.serverList, e => e.id, e => html`
|
||||
<outline-server-view
|
||||
.id="${this.makeViewId(e.id)}"
|
||||
.language="${this.language}"
|
||||
.localize="${this.localize}"
|
||||
?hidden="${e.id !== this.selectedServerId}">
|
||||
</outline-server-view>
|
||||
`)}</div>`;
|
||||
}
|
||||
|
||||
async getServerView(serverId: string): Promise<ServerView> {
|
||||
if (!serverId) {
|
||||
return null;
|
||||
}
|
||||
// We need to wait updates to be completed or the view may not yet be there.
|
||||
await this.updateComplete;
|
||||
const selector = `#${this.makeViewId(serverId)}`;
|
||||
return this.shadowRoot.querySelector<ServerView>(selector);
|
||||
}
|
||||
|
||||
// Wrapper to encode a string in base64. This is necessary to set the server view IDs to
|
||||
// the display server IDs, which are URLs, so they can be used with selector methods. The IDs
|
||||
// are never decoded.
|
||||
private makeViewId(serverId: string): string {
|
||||
return `serverView-${btoa(serverId).replace(/=/g, '')}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -493,7 +493,7 @@ export class ServerView extends DirMixin(PolymerElement) {
|
|||
<img class="digital-ocean-icon" src="images/do_white_logo.svg">
|
||||
</div>
|
||||
<div class="stats">
|
||||
<h3>[[managedServerUtilzationPercentage]]</h3>
|
||||
<h3>[[_computeManagedServerUtilzationPercentage(totalInboundBytes, monthlyOutboundTransferBytes)]]</h3>
|
||||
<p>/[[_formatBytesTransferred(monthlyOutboundTransferBytes, language)]]</p>
|
||||
</div>
|
||||
<p>[[localize('server-data-used')]]</p>
|
||||
|
|
@ -623,38 +623,33 @@ export class ServerView extends DirMixin(PolymerElement) {
|
|||
serverName: String,
|
||||
serverHostname: String,
|
||||
serverVersion: String,
|
||||
isHostnameEditable: {type: Boolean},
|
||||
isHostnameEditable: Boolean,
|
||||
serverManagementApiUrl: String,
|
||||
serverPortForNewAccessKeys: Number,
|
||||
isAccessKeyPortEditable: {type: Boolean},
|
||||
serverCreationDate: {type: Date},
|
||||
isAccessKeyPortEditable: Boolean,
|
||||
serverCreationDate: Date,
|
||||
serverLocation: String,
|
||||
defaultDataLimitBytes: Number,
|
||||
isDefaultDataLimitEnabled: {type: Boolean},
|
||||
supportsDefaultDataLimit: {type: Boolean},
|
||||
showFeatureMetricsDisclaimer: {type: Boolean},
|
||||
isDefaultDataLimitEnabled: Boolean,
|
||||
supportsDefaultDataLimit: Boolean,
|
||||
showFeatureMetricsDisclaimer: Boolean,
|
||||
isServerManaged: Boolean,
|
||||
isServerReachable: Boolean,
|
||||
retryDisplayingServer: Function,
|
||||
myConnection: Object,
|
||||
totalInboundBytes: Number,
|
||||
baselineDataTransfer: Number,
|
||||
accessKeyRows: {type: Array},
|
||||
accessKeyRows: Array,
|
||||
hasNonAdminAccessKeys: Boolean,
|
||||
metricsEnabled: Boolean,
|
||||
monthlyOutboundTransferBytes: {type: Number},
|
||||
monthlyCost: {type: Number},
|
||||
managedServerUtilzationPercentage: {
|
||||
type: Number,
|
||||
computed:
|
||||
'_computeManagedServerUtilzationPercentage(totalInboundBytes, monthlyOutboundTransferBytes)',
|
||||
},
|
||||
accessKeySortBy: {type: String},
|
||||
accessKeySortDirection: {type: Number},
|
||||
language: {type: String},
|
||||
localize: {type: Function, readonly: true},
|
||||
selectedPage: {type: String},
|
||||
selectedTab: {type: String},
|
||||
monthlyOutboundTransferBytes: Number,
|
||||
monthlyCost: Number,
|
||||
accessKeySortBy: String,
|
||||
accessKeySortDirection: Number,
|
||||
language: String,
|
||||
localize: Function,
|
||||
selectedPage: String,
|
||||
selectedTab: String,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue