diff --git a/src/generic_core/uproxy_core.ts b/src/generic_core/uproxy_core.ts index be9076e1c4..45c65635d4 100644 --- a/src/generic_core/uproxy_core.ts +++ b/src/generic_core/uproxy_core.ts @@ -610,7 +610,7 @@ export class uProxyCore implements uproxy_core_api.CoreApi { ui.update(uproxy_core_api.Update.CORE_UPDATE_AVAILABLE, details); } - public cloudInstall = (args:uproxy_core_api.CloudInstallArgs): Promise => { + public cloudInstall = (args:uproxy_core_api.CloudInstallArgs): Promise => { if (args.providerName !== 'digitalocean') { return Promise.reject(new Error('unsupported cloud provider')); } @@ -655,15 +655,62 @@ export class uProxyCore implements uproxy_core_api.CoreApi { }; const MAX_INSTALLS = 5; return retry(install, MAX_INSTALLS); - }).then((output: string) => { + }).then((cloudNetworkData :any) => { + // TODO: make cloudNetworkData an Invite type. This requires the cloud + // social provider to export the Invite interface, and also to cleanup + // reference paths so that uProxy doesn't need to include modules like + // ssh2. destroyModules(); - return { - invite: output - }; + + // Login to the Cloud network if the user isn't already so that + // we can add the new cloud server to the roster. + return this.loginIfNeeded_('Cloud').then((cloudNetwork) => { + // Set flag so Cloud social provider knows this invite is for the admin + // user, who just created the server. + cloudNetworkData['isAdmin'] = true; + + // Synthesize an invite token based on cloudNetworkData. + // CONSIDER: if we had a social.acceptInvitation that only took + // networkData we wouldn't need to fake the other fields. + return cloudNetwork.acceptInvitation({ + v: 2, + networkName: 'Cloud', + userName: cloudNetworkData['host'], + networkData: JSON.stringify(cloudNetworkData) + }); + }); }, (e: Error) => { destroyModules(); return Promise.reject(e); }); + } // end of cloudInstall + + // Gets a social.Network, and logs the user in if they aren't yet logged in. + private loginIfNeeded_ = (networkName :string) : Promise => { + let network = this.getNetworkByName_(networkName); + if (network) { + return Promise.resolve(network); + } + + // User is not yet logged in. + return this.login({ + network: networkName, + loginType: uproxy_core_api.LoginType.INITIAL + }).then(() => { + return this.getNetworkByName_(networkName); + }); + } + + // The social_network module in theory should support multiple userIds + // being logged into the same network. However that has never been tested + // and is not used by the rest of uProxy code. This method just returns + // the first (and currently only) network for the given networkName, or null + // if the network is not logged in. + private getNetworkByName_ = (networkName :string) : social.Network => { + for (var userId in social_network.networks[networkName]) { + return social_network.networks[networkName][userId]; + } + return null; } public updateOrgPolicy(policy :Object) :void { diff --git a/src/generic_ui/polymer/cloud-install.html b/src/generic_ui/polymer/cloud-install.html index 71b07cdcff..e210ff59d9 100644 --- a/src/generic_ui/polymer/cloud-install.html +++ b/src/generic_ui/polymer/cloud-install.html @@ -1,6 +1,5 @@ - @@ -100,14 +99,7 @@

Create a cloud server

Create a cloud server

-

Your cloud server is ready!

-

Below is the URL to administer your new server. Copy and paste it into a file and save it somewhere you'll be able to get to it in the future. Think of it as a password: keep it safe and don't share it with anyone you don't trust.

-

Once you've saved the URL, open it in your browser to add your new server to your contacts list.

-
- - - -
+

Your cloud server is ready and has been added to your friends list.

{{ 'OK' | $$ }}
diff --git a/src/generic_ui/polymer/cloud-install.ts b/src/generic_ui/polymer/cloud-install.ts index b42373a02d..3884014168 100644 --- a/src/generic_ui/polymer/cloud-install.ts +++ b/src/generic_ui/polymer/cloud-install.ts @@ -21,20 +21,15 @@ Polymer({ }, loginTapped: function() { this.closeDialogs(); - // TODO: show the dialog when this value changes, not this nasty hack ui.cloudInstallStatus = ''; this.$.installingDialog.open(); ui.cloudInstall({ providerName: DEFAULT_PROVIDER, region: this.$.regionMenu.selected - }).then((result: uproxy_core_api.CloudInstallResult) => { - this.inviteUrl = result.invite; + }).then(() => { this.closeDialogs(); this.$.successDialog.open(); - - // TODO: In addition to displaying the URL so the user can store it somewhere - // we should add the new server to the user's contact list. }).catch((e: Error) => { // TODO: Figure out which fields in e are set, because message isn't. this.closeDialogs(); @@ -47,6 +42,5 @@ Polymer({ }, ready: function() { this.ui = ui; - this.inviteUrl = ''; } }); diff --git a/src/generic_ui/scripts/core_connector.ts b/src/generic_ui/scripts/core_connector.ts index 8b8f4c4651..49e27351cb 100644 --- a/src/generic_ui/scripts/core_connector.ts +++ b/src/generic_ui/scripts/core_connector.ts @@ -247,8 +247,8 @@ class CoreConnector implements uproxy_core_api.CoreApi { return this.promiseCommand(uproxy_core_api.Command.ACCEPT_INVITATION, data); } - cloudInstall = (args:uproxy_core_api.CloudInstallArgs): Promise => { - return this.promiseCommand(uproxy_core_api.Command.CLOUD_INSTALL, args); + cloudInstall = (args:uproxy_core_api.CloudInstallArgs): Promise => { + return this.promiseCommand(uproxy_core_api.Command.CLOUD_INSTALL, args); } } // class CoreConnector diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index c06179bd01..c023d99d06 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -1030,7 +1030,6 @@ export class UserInterface implements ui_constants.UiApi { // Update the user's category in both get and share tabs. model.categorizeUser(user, this.model.contacts.getAccessContacts, oldUserCategories.getTab, newUserCategories.getTab); - if (user.status != social.UserStatus.CLOUD_INSTANCE_SHARED_WITH_LOCAL) { model.categorizeUser(user, this.model.contacts.shareAccessContacts, oldUserCategories.shareTab, newUserCategories.shareTab); @@ -1353,7 +1352,7 @@ export class UserInterface implements ui_constants.UiApi { return i18nMessage.replace(/<((?!(\/?(strong|a|p|br|uproxy-faq-link)))[^>]+)>/g, ''); } - public cloudInstall = (args:uproxy_core_api.CloudInstallArgs): Promise => { + public cloudInstall = (args:uproxy_core_api.CloudInstallArgs): Promise => { return this.core.cloudInstall(args); } } // class UserInterface diff --git a/src/interfaces/uproxy_core_api.ts b/src/interfaces/uproxy_core_api.ts index 25f3b6cdbd..47f3dfabf6 100644 --- a/src/interfaces/uproxy_core_api.ts +++ b/src/interfaces/uproxy_core_api.ts @@ -139,7 +139,6 @@ export enum Update { PORT_CONTROL_STATUS = 2025, // Payload is a string, obtained from the SignalBatcher in uproxy-lib. ONETIME_MESSAGE = 2026, - // Payload is a CloudInstallResult. CLOUD_INSTALL_STATUS = 2027 } @@ -224,13 +223,6 @@ export interface CloudInstallArgs { region: string; }; -// Output of #cloudInstall. -export interface CloudInstallResult { - // Invitation URL, iff the the server was successfully created - // and provisioned. - invite: string; -}; - /** * The primary interface to the uProxy Core. * @@ -297,6 +289,6 @@ export interface CoreApi { // callers should expose CLOUD_INSTALL_STATUS updates to the user. // This may also invoke an OAuth flow, in order to perform operations // with the cloud computing provider on the user's behalf. - cloudInstall(args:CloudInstallArgs): Promise; + cloudInstall(args:CloudInstallArgs): Promise; }