diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index a0a777414..cdf26c7df 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -3495,11 +3495,24 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }); }; + Client.prototype.registerCloudronWithSetupToken = function (setupToken, callback) { + var data = { + setupToken: setupToken + }; + + post('/api/v1/appstore/register_cloudron_with_setup_token', data, null, function (error, data, status) { + if (error) return callback(error); + if (status !== 201) return callback(new ClientError(status, data)); + + callback(null); + }); + }; + Client.prototype.registerCloudron = function (email, password, totpToken, signup, callback) { var data = { email: email, password: password, - signup: signup, + signup: signup }; if (totpToken) data.totpToken = totpToken; diff --git a/dashboard/src/views/appstore.html b/dashboard/src/views/appstore.html index d6089c72d..0253d1211 100644 --- a/dashboard/src/views/appstore.html +++ b/dashboard/src/views/appstore.html @@ -281,8 +281,9 @@
-

{{ 'appstore.accountDialog.titleSignUp' | tr }}

-

{{ 'appstore.accountDialog.titleLogin' | tr }}

+

{{ 'appstore.accountDialog.titleSignUp' | tr }}

+

{{ 'appstore.accountDialog.titleLogin' | tr }}

+

{{ 'appstore.accountDialog.titleToken' | tr }}

{{ 'appstore.accountDialog.description' | tr }}

@@ -293,54 +294,122 @@

-
- +
+ + -
+
+ + +
+ {{ appstoreLogin.error.email }} +
+
+ +
+ + +
+ {{ 'appstore.accountDialog.errorWrongPassword' | tr }} +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +
- {{ appstoreLogin.error.email }} + {{ appstoreLogin.error.email }}
-
+
-
+
- {{ 'appstore.accountDialog.errorWrongPassword' | tr }} + {{ 'appstore.accountDialog.errorWrongPassword' | tr }}
-
+
-
+
- {{ appstoreLogin.error.totpToken }} + {{ appstoreLogin.error.totpToken }}
-
+
-
- -
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+ + +
+ {{ appstoreLogin.error.setupToken }} +
+
+ +
+ +
+ +
+ +
+ +
+
+
+

+
-
- - -
-
- - {{ 'appstore.accountDialog.switchToSignUpAction' | tr }} - {{ 'appstore.accountDialog.switchToLoginAction' | tr }} -
- - + {{ 'appstore.accountDialog.switchToSignUpAction' | tr }} + {{ 'appstore.accountDialog.switchToLoginAction' | tr }} + or use a setup token +
diff --git a/dashboard/src/views/appstore.js b/dashboard/src/views/appstore.js index 6f46c5543..c50d6dbd1 100644 --- a/dashboard/src/views/appstore.js +++ b/dashboard/src/views/appstore.js @@ -416,14 +416,16 @@ angular.module('Application').controller('AppStoreController', ['$scope', '$tran email: '', password: '', totpToken: '', - register: true, + setupType: 'login', termsAccepted: false, + setupToken: '', submit: function () { $scope.appstoreLogin.error = {}; $scope.appstoreLogin.busy = true; - Client.registerCloudron($scope.appstoreLogin.email, $scope.appstoreLogin.password, $scope.appstoreLogin.totpToken, $scope.appstoreLogin.register, function (error) { + var func = $scope.appstoreLogin.setupToken ? Client.registerCloudronWithSetupToken.bind(null, $scope.appstoreLogin.setupToken) : Client.registerCloudron.bind(null, $scope.appstoreLogin.email, $scope.appstoreLogin.password, $scope.appstoreLogin.totpToken, $scope.appstoreLogin.register); + func(function (error) { if (error) { $scope.appstoreLogin.busy = false; diff --git a/src/appstore.js b/src/appstore.js index c0ad2c78d..c9db26771 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -13,7 +13,8 @@ exports = module.exports = { getAppVersion, downloadIcon, - registerWithLoginCredentials, + registerCloudronWithSetupToken, + registerCloudronWithLogin, updateCloudron, purchaseApp, @@ -295,10 +296,10 @@ async function getAppUpdate(app, options) { async function registerCloudron(data) { assert.strictEqual(typeof data, 'object'); - const { domain, accessToken, version, existingApps } = data; + const { domain, setupToken, accessToken, version, existingApps } = data; const [error, response] = await safe(superagent.post(`${await getApiServerOrigin()}/api/v1/register_cloudron`) - .send({ domain, accessToken, version, existingApps }) + .send({ domain, setupToken, accessToken, version, existingApps }) .timeout(30 * 1000) .ok(() => true)); @@ -341,13 +342,26 @@ async function updateCloudron(data) { debug(`updateCloudron: Cloudron updated with data ${JSON.stringify(data)}`); } -async function registerWithLoginCredentials(options) { +async function registerCloudronWithSetupToken(options) { + assert.strictEqual(typeof options, 'object'); + + const { domain } = await dashboard.getLocation(); + + await registerCloudron({ domain, setupToken: options.setupToken, version: constants.VERSION }); + + for (const app of await apps.list()) { + await purchaseApp({ appId: app.id, appstoreId: app.appStoreId, manifestId: app.manifest.id || 'customapp' }); + } +} + +async function registerCloudronWithLogin(options) { assert.strictEqual(typeof options, 'object'); if (options.signup) await registerUser(options.email, options.password); - const result = await login(options.email, options.password, options.totpToken || ''); + const { domain } = await dashboard.getLocation(); + await registerCloudron({ domain, accessToken: result.accessToken, version: constants.VERSION }); for (const app of await apps.list()) { diff --git a/src/routes/appstore.js b/src/routes/appstore.js index 44724ad29..002e2a7ba 100644 --- a/src/routes/appstore.js +++ b/src/routes/appstore.js @@ -6,7 +6,8 @@ exports = module.exports = { getAppVersion, getWebToken, - registerCloudron, + registerCloudronWithSetupToken, + registerCloudronWithLogin, getSubscription }; @@ -52,7 +53,18 @@ async function getWebToken(req, res, next) { next(new HttpSuccess(200, { accessToken })); } -async function registerCloudron(req, res, next) { +async function registerCloudronWithSetupToken(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.setupToken !== 'string') return next(new HttpError(400, 'setupToken must be a string')); + + const [error] = await safe(appstore.registerCloudronWithSetupToken({ setupToken: req.body.setupToken })); + if (error) return next(BoxError.toHttpError(error)); + + next(new HttpSuccess(201, {})); +} + +async function registerCloudronWithLogin(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.email !== 'string' || !req.body.email) return next(new HttpError(400, 'email must be string')); @@ -60,7 +72,7 @@ async function registerCloudron(req, res, next) { if ('totpToken' in req.body && typeof req.body.totpToken !== 'string') return next(new HttpError(400, 'totpToken must be string')); if (typeof req.body.signup !== 'boolean') return next(new HttpError(400, 'signup must be a boolean')); - const [error] = await safe(appstore.registerWithLoginCredentials(req.body)); + const [error] = await safe(appstore.registerCloudronWithLogin(req.body)); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, {})); diff --git a/src/server.js b/src/server.js index d617154d3..ae622d02b 100644 --- a/src/server.js +++ b/src/server.js @@ -219,7 +219,8 @@ async function initializeExpressSync() { router.post('/api/v1/directory_server/config', json, token, authorizeAdmin, routes.directoryServer.setConfig); // appstore and subscription routes - router.post('/api/v1/appstore/register_cloudron', json, token, authorizeOwner, routes.appstore.registerCloudron); + router.post('/api/v1/appstore/register_cloudron', json, token, authorizeOwner, routes.appstore.registerCloudronWithLogin); + router.post('/api/v1/appstore/register_cloudron_with_setup_token', json, token, authorizeOwner, routes.appstore.registerCloudronWithSetupToken); router.get ('/api/v1/appstore/web_token', json, token, authorizeOwner, routes.appstore.getWebToken); router.get ('/api/v1/appstore/subscription', token, authorizeUser, routes.appstore.getSubscription); // for all users router.get ('/api/v1/appstore/apps', token, authorizeAdmin, routes.appstore.getApps);