mirror of
https://github.com/OutlineFoundation/outline-server.git
synced 2026-08-04 14:37:34 +00:00
Refactor
This commit is contained in:
parent
e3fda57b25
commit
1295d3d9e7
7 changed files with 53 additions and 54 deletions
|
|
@ -49,7 +49,7 @@ export type Account = Readonly<{
|
|||
email: string;
|
||||
uuid: string;
|
||||
email_verified: boolean;
|
||||
status: string;
|
||||
status: 'active' | 'warning' | 'locked';
|
||||
status_message: string;
|
||||
}>;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@
|
|||
"error-do-account-info": "Failed to get DigitalOcean account information",
|
||||
"error-do-auth": "Authentication with DigitalOcean failed",
|
||||
"error-do-regions": "Failed to get list of available regions",
|
||||
"error-do-limit": "Your DigitalOcean account has reached its Droplet limit. You can request an increase at https://cloud.digitalocean.com/account/team/droplet_limit_increase",
|
||||
"error-do-warning": "DigitalOcean warning:",
|
||||
"error-gcp-auth": "Authentication with Google Cloud Platform failed",
|
||||
"error-feedback": "Failed to submit feedback. Please try again.",
|
||||
"error-hostname-invalid": "Must be an IP address or valid hostname.",
|
||||
|
|
|
|||
|
|
@ -319,6 +319,14 @@
|
|||
"message": "Failed to get list of available regions",
|
||||
"description": "This string appears in an error notification toast. It is shown when there is an error retrieving the regions available for server deployment."
|
||||
},
|
||||
"error_do_limit": {
|
||||
"message": "Your DigitalOcean account has reached its Droplet limit. You can request an increase at https://cloud.digitalocean.com/account/team/droplet_limit_increase",
|
||||
"description": "This string appears in an error notification toast. It is shown when the user has created the maximum number of allowed servers."
|
||||
},
|
||||
"error_do_warning": {
|
||||
"message": "DigitalOcean warning:",
|
||||
"description": "This string appears at the start of an error notification toast. It is followed by an English-language error description in quotes."
|
||||
},
|
||||
"error_gcp_auth": {
|
||||
"message": "Authentication with Google Cloud Platform failed",
|
||||
"description": "This string appears in an error notification toast. It is shown when there is an error when logging in to the user's Google Cloud Platform account. Google Cloud Platform is a cloud server provider name and should not be translated."
|
||||
|
|
|
|||
|
|
@ -38,15 +38,10 @@ export interface RegionOption extends location.CloudLocationOption {
|
|||
readonly cloudLocation: Region;
|
||||
}
|
||||
|
||||
export enum Status {
|
||||
ACTIVE,
|
||||
EMAIL_UNVERIFIED,
|
||||
MISSING_BILLING_INFORMATION,
|
||||
}
|
||||
|
||||
export interface AccountInfo {
|
||||
status: Status;
|
||||
warning?: string;
|
||||
export interface Status {
|
||||
readonly needsBillingInfo: boolean;
|
||||
readonly needsEmailVerification: boolean;
|
||||
readonly warning?: string;
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
|
|
@ -55,7 +50,7 @@ export interface Account {
|
|||
// Returns a user-friendly name (email address) associated with the account.
|
||||
getName(): Promise<string>;
|
||||
// Returns the status of the account.
|
||||
getStatus(): Promise<AccountInfo>;
|
||||
getStatus(): Promise<Status>;
|
||||
// Lists all existing Shadowboxes. If `fetchFromHost` is true, performs a network request to
|
||||
// retrieve the servers; otherwise resolves with a cached server list.
|
||||
listServers(fetchFromHost?: boolean): Promise<ManagedServer[]>;
|
||||
|
|
@ -64,6 +59,7 @@ export interface Account {
|
|||
// Creates a server and returning it when it becomes active (i.e. the server has
|
||||
// created, not necessarily once shadowbox installation has finished).
|
||||
createServer(region: Region, name: string): Promise<ManagedServer>;
|
||||
|
||||
// Returns true if the account has reached its Droplet limit and
|
||||
// will not be allowed to create new droplets.
|
||||
hasReachedLimit(): Promise<boolean>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,10 +353,13 @@ export class App {
|
|||
id: this.digitalOceanAccount.getId(),
|
||||
name: await this.digitalOceanAccount.getName(),
|
||||
};
|
||||
const info = await this.digitalOceanAccount.getStatus();
|
||||
if (info.status !== digitalocean.Status.ACTIVE) {
|
||||
const status = await this.digitalOceanAccount.getStatus();
|
||||
if (status.needsBillingInfo || status.needsEmailVerification) {
|
||||
return [];
|
||||
}
|
||||
if (status.warning) {
|
||||
this.showDigitalOceanWarning(status.warning);
|
||||
}
|
||||
const servers = await this.digitalOceanAccount.listServers();
|
||||
for (const server of servers) {
|
||||
this.addServer(this.digitalOceanAccount.getId(), server);
|
||||
|
|
@ -370,6 +373,10 @@ export class App {
|
|||
return [];
|
||||
}
|
||||
|
||||
private showDigitalOceanWarning(warning: string) {
|
||||
this.appRoot.showError(`${this.appRoot.localize('error-do-warning')} ${warning}`);
|
||||
}
|
||||
|
||||
private async loadGcpAccount(gcpAccount: gcp.Account): Promise<server.ManagedServer[]> {
|
||||
if (!gcpAccount) {
|
||||
return [];
|
||||
|
|
@ -505,16 +512,20 @@ export class App {
|
|||
};
|
||||
const oauthUi = this.appRoot.getDigitalOceanOauthFlow(signOutAction);
|
||||
for (;;) {
|
||||
const info = await this.digitalOceanRetry(async () => {
|
||||
const status = await this.digitalOceanRetry(async () => {
|
||||
if (cancelled) {
|
||||
throw CANCELLED_ERROR;
|
||||
}
|
||||
return await digitalOceanAccount.getStatus();
|
||||
});
|
||||
if (info.warning) {
|
||||
this.appRoot.showError(`DigitalOcean warning: ${info.warning}`);
|
||||
}
|
||||
if (info.status === digitalocean.Status.ACTIVE) {
|
||||
if (status.needsBillingInfo) {
|
||||
oauthUi.showBilling();
|
||||
} else if (status.needsEmailVerification) {
|
||||
oauthUi.showEmailVerification();
|
||||
} else {
|
||||
if (status.warning) {
|
||||
this.showDigitalOceanWarning(status.warning);
|
||||
}
|
||||
bringToFront();
|
||||
if (activatingAccount) {
|
||||
// Show the 'account active' screen for a few seconds if the account was activated
|
||||
|
|
@ -526,11 +537,6 @@ export class App {
|
|||
}
|
||||
this.appRoot.showDigitalOceanOauthFlow();
|
||||
activatingAccount = true;
|
||||
if (info.status === digitalocean.Status.MISSING_BILLING_INFORMATION) {
|
||||
oauthUi.showBilling();
|
||||
} else {
|
||||
oauthUi.showEmailVerification();
|
||||
}
|
||||
await sleep(1000);
|
||||
if (this.appRoot.currentPage !== 'digitalOceanOauth') {
|
||||
// The user navigated away.
|
||||
|
|
@ -725,10 +731,8 @@ export class App {
|
|||
|
||||
try {
|
||||
if (await digitalOceanAccount.hasReachedLimit()) {
|
||||
this.appRoot.showError(
|
||||
'Your DigitalOcean account has reached its Droplet limit. You can request an increase at https://cloud.digitalocean.com/account/team/droplet_limit_increase'
|
||||
);
|
||||
return;
|
||||
this.appRoot.showError(this.appRoot.localize('error-do-limit'));
|
||||
return; // Don't proceed to the region picker.
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to check droplet limit status', e);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {Account, DigitalOceanSession, DropletInfo, RestApiSession} from '../cloud/digitalocean_api';
|
||||
import {DigitalOceanSession, DropletInfo, RestApiSession} from '../cloud/digitalocean_api';
|
||||
import * as crypto from '../infrastructure/crypto';
|
||||
import * as do_install_script from '../install_scripts/do_install_script';
|
||||
import * as digitalocean from '../model/digitalocean';
|
||||
|
|
@ -46,31 +46,20 @@ export class DigitalOceanAccount implements digitalocean.Account {
|
|||
return (await this.digitalOcean.getAccount())?.email;
|
||||
}
|
||||
|
||||
async getStatus(): Promise<digitalocean.AccountInfo> {
|
||||
async getStatus(): Promise<digitalocean.Status> {
|
||||
const account = await this.digitalOcean.getAccount();
|
||||
return {
|
||||
status: await this.summarizeStatus(account),
|
||||
warning: account.status !== 'active' ? account.status_message : '',
|
||||
};
|
||||
}
|
||||
|
||||
private async summarizeStatus(account: Account): Promise<digitalocean.Status> {
|
||||
if (account.status === 'active') {
|
||||
return digitalocean.Status.ACTIVE;
|
||||
const needsEmailVerification = !account.email_verified;
|
||||
let needsBillingInfo = false;
|
||||
if (!needsEmailVerification && account.status === 'locked') {
|
||||
// If the account is locked for no discernible reason, and there are no droplets,
|
||||
// assume the billing info is missing.
|
||||
const droplets = await this.digitalOcean.getDroplets();
|
||||
if (droplets.length == 0) {
|
||||
needsBillingInfo = true;
|
||||
}
|
||||
}
|
||||
if (!account.email_verified) {
|
||||
return digitalocean.Status.EMAIL_UNVERIFIED;
|
||||
}
|
||||
if (account.status === 'warning') {
|
||||
return digitalocean.Status.ACTIVE;
|
||||
}
|
||||
const servers = await this.digitalOcean.getDroplets();
|
||||
if (servers.length > 0) {
|
||||
// The account is locked, but it has droplets so it must have
|
||||
// been activated with billing information.
|
||||
return digitalocean.Status.ACTIVE;
|
||||
}
|
||||
return digitalocean.Status.MISSING_BILLING_INFORMATION;
|
||||
const warning = account.status !== 'active' ? account.status_message : '';
|
||||
return {needsBillingInfo, needsEmailVerification, warning};
|
||||
}
|
||||
|
||||
// Return a list of regions indicating whether they are available and support
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ export class FakeDigitalOceanAccount implements digitalocean.Account {
|
|||
async getName(): Promise<string> {
|
||||
return 'fake-digitalocean-account-name';
|
||||
}
|
||||
async getStatus(): Promise<digitalocean.AccountInfo> {
|
||||
return {status: digitalocean.Status.ACTIVE};
|
||||
async getStatus(): Promise<digitalocean.Status> {
|
||||
return {needsBillingInfo: false, needsEmailVerification: false};
|
||||
}
|
||||
listServers() {
|
||||
return Promise.resolve(this.servers);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue