mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
publish, archive, access keys and login updated
This commit is contained in:
parent
10727c5d79
commit
396febb306
@ -133,6 +133,8 @@ class Service {
|
||||
that.service = serverlessFile.service;
|
||||
}
|
||||
|
||||
that.app = serverlessFile.app;
|
||||
that.tenant = serverlessFile.tenant;
|
||||
that.custom = serverlessFile.custom;
|
||||
that.plugins = serverlessFile.plugins;
|
||||
that.resources = serverlessFile.resources;
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const forge = require('node-forge');
|
||||
|
||||
module.exports = (encryptedToken, key, iv) => {
|
||||
const decipherToken = forge.cipher.createDecipher('AES-CBC', key);
|
||||
decipherToken.start({ iv });
|
||||
decipherToken.update(forge.util.createBuffer(forge.util.decode64(encryptedToken)));
|
||||
const result = decipherToken.finish(); // check 'result' for true/false
|
||||
if (!result) {
|
||||
throw new Error(`Couldn't decrypt token: ${encryptedToken}`);
|
||||
}
|
||||
return decipherToken.output.toString();
|
||||
};
|
||||
@ -1,23 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const expect = require('chai').expect;
|
||||
const forge = require('node-forge');
|
||||
const decryptToken = require('./decryptToken');
|
||||
|
||||
const encryptAuthToken = (token, key, iv) => {
|
||||
const cipher = forge.cipher.createCipher('AES-CBC', key);
|
||||
cipher.start({ iv });
|
||||
cipher.update(forge.util.createBuffer(token));
|
||||
cipher.finish();
|
||||
return forge.util.encode64(cipher.output.data);
|
||||
};
|
||||
|
||||
describe('#decryptToken()', () => {
|
||||
it('should encrypt a token with AWS CBC', () => {
|
||||
const token = 'f3120811-8306-4d67-98e5-13fde2e490e4';
|
||||
const key = forge.random.getBytesSync(16);
|
||||
const iv = forge.random.getBytesSync(16);
|
||||
const encryptedToken = encryptAuthToken(token, key, iv);
|
||||
expect(decryptToken(encryptedToken, key, iv)).to.equal(token);
|
||||
});
|
||||
});
|
||||
@ -1,20 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const gql = require('graphql-tag');
|
||||
|
||||
module.exports = (id, apolloQueryFn) =>
|
||||
apolloQueryFn({
|
||||
fetchPolicy: 'network-only',
|
||||
query: gql`
|
||||
query cliLoginById($id: String!) {
|
||||
cliLoginById(id: $id) {
|
||||
encryptedAccessToken
|
||||
encryptedIdToken
|
||||
encryptedRefreshToken
|
||||
encryptedKey
|
||||
encryptedIv
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { id },
|
||||
}).then(response => response.data);
|
||||
@ -1,32 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const sinon = require('sinon');
|
||||
const expect = require('chai').expect;
|
||||
const gql = require('graphql-tag');
|
||||
const getCliLoginById = require('./getCliLoginById');
|
||||
|
||||
describe('#getCliLoginById()', () => {
|
||||
it('should query for the cliLoginById', () => {
|
||||
const expectedParams = {
|
||||
fetchPolicy: 'network-only',
|
||||
query: gql`
|
||||
query cliLoginById($id: String!) {
|
||||
cliLoginById(id: $id) {
|
||||
encryptedAccessToken
|
||||
encryptedIdToken
|
||||
encryptedRefreshToken
|
||||
encryptedKey
|
||||
encryptedIv
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { id: 'abcd' },
|
||||
};
|
||||
|
||||
const query = sinon.stub().resolves({ data: { cliLoginId: 'abcd' } });
|
||||
return getCliLoginById('abcd', query).then(data => {
|
||||
expect(data).to.deep.equal({ cliLoginId: 'abcd' });
|
||||
expect(query.getCall(0).args[0]).to.deep.equal(expectedParams);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -2,26 +2,9 @@
|
||||
|
||||
const BbPromise = require('bluebird');
|
||||
const jwtDecode = require('jwt-decode');
|
||||
const chalk = require('chalk');
|
||||
const uuid = require('uuid');
|
||||
const has = require('lodash/has');
|
||||
const forge = require('node-forge');
|
||||
const querystring = require('querystring');
|
||||
const openBrowser = require('../../utils/openBrowser');
|
||||
const platform = require('@serverless/platform-sdk');
|
||||
const configUtils = require('../../utils/config');
|
||||
const clearConsole = require('../../utils/clearConsole');
|
||||
const userStats = require('../../utils/userStats');
|
||||
const setConfig = require('../../utils/config').set;
|
||||
const createApolloClient = require('../../utils/createApolloClient');
|
||||
const decryptToken = require('./lib/decryptToken');
|
||||
const getCliLoginById = require('./lib/getCliLoginById');
|
||||
|
||||
const config = {
|
||||
PLATFORM_FRONTEND_BASE_URL: 'https://platform.serverless.com/',
|
||||
GRAPHQL_ENDPOINT_URL: 'https://graphql.serverless.com/graphql',
|
||||
};
|
||||
|
||||
const client = createApolloClient(config.GRAPHQL_ENDPOINT_URL);
|
||||
|
||||
class Login {
|
||||
constructor(serverless, options) {
|
||||
@ -41,136 +24,53 @@ class Login {
|
||||
};
|
||||
}
|
||||
login() {
|
||||
clearConsole();
|
||||
this.serverless.cli.log('The Serverless login will open in your default browser...');
|
||||
const configuration = configUtils.getConfig();
|
||||
const frameworkId = configuration.frameworkId;
|
||||
const cliLoginId = uuid.v4();
|
||||
let fetchTries = 0;
|
||||
|
||||
forge.pki.rsa.generateKeyPair({ bits: 2048 }, (generateKeyErr, keypair) => {
|
||||
if (generateKeyErr) {
|
||||
this.serverless.cli.log(
|
||||
'Sorry, failed initiating the authentication key. ',
|
||||
'Please contact the Serverless team at hello@serverless.com'
|
||||
);
|
||||
throw generateKeyErr;
|
||||
}
|
||||
const publicKeyPem = forge.pki.publicKeyToPem(keypair.publicKey);
|
||||
const encodedPublicKeyPem = forge.util.encode64(publicKeyPem);
|
||||
|
||||
this.serverless.cli.log('Opening browser...');
|
||||
|
||||
// Avoid camel casing since queries is going into to be part of the URL
|
||||
const queries = querystring.stringify({
|
||||
cli: 'v1',
|
||||
'login-id': cliLoginId,
|
||||
'public-key': encodedPublicKeyPem,
|
||||
});
|
||||
// open default browser
|
||||
openBrowser(`${config.PLATFORM_FRONTEND_BASE_URL}login?${queries}`);
|
||||
|
||||
const fetchCliLogin = () => {
|
||||
fetchTries += 1;
|
||||
// indicating the user after a while that the CLI is still waiting
|
||||
if (fetchTries >= 60) {
|
||||
this.serverless.cli.log('Waiting for a successful authentication in the browser.');
|
||||
fetchTries = 0;
|
||||
}
|
||||
getCliLoginById(cliLoginId, client.query)
|
||||
.then(response => {
|
||||
const hasAllToken =
|
||||
has(response, ['cliLoginById', 'encryptedAccessToken']) &&
|
||||
has(response, ['cliLoginById', 'encryptedIdToken']) &&
|
||||
has(response, ['cliLoginById', 'encryptedRefreshToken']) &&
|
||||
has(response, ['cliLoginById', 'encryptedKey']) &&
|
||||
has(response, ['cliLoginById', 'encryptedIv']) &&
|
||||
response.cliLoginById.encryptedAccessToken !== null &&
|
||||
response.cliLoginById.encryptedIdToken !== null &&
|
||||
response.cliLoginById.encryptedRefreshToken !== null &&
|
||||
response.cliLoginById.encryptedKey !== null &&
|
||||
response.cliLoginById.encryptedIv !== null;
|
||||
if (!hasAllToken) {
|
||||
// delay the requests for a bit
|
||||
setTimeout(() => fetchCliLogin(), 500);
|
||||
} else {
|
||||
const key = keypair.privateKey.decrypt(
|
||||
forge.util.decode64(response.cliLoginById.encryptedKey),
|
||||
'RSA-OAEP'
|
||||
);
|
||||
const iv = keypair.privateKey.decrypt(
|
||||
forge.util.decode64(response.cliLoginById.encryptedIv),
|
||||
'RSA-OAEP'
|
||||
);
|
||||
|
||||
const idToken = decryptToken(response.cliLoginById.encryptedIdToken, key, iv);
|
||||
const accessToken = decryptToken(response.cliLoginById.encryptedAccessToken, key, iv);
|
||||
const refreshToken = decryptToken(
|
||||
response.cliLoginById.encryptedRefreshToken,
|
||||
key,
|
||||
iv
|
||||
);
|
||||
|
||||
const decoded = jwtDecode(idToken);
|
||||
this.serverless.cli.log('You are now logged in');
|
||||
// because platform only support github
|
||||
const id = decoded.tracking_id || decoded.sub;
|
||||
const userConfig = {
|
||||
userId: id,
|
||||
frameworkId,
|
||||
users: {},
|
||||
};
|
||||
// set user auth in global .serverlessrc file
|
||||
userConfig.users[id] = {
|
||||
userId: id,
|
||||
name: decoded.name,
|
||||
email: decoded.email,
|
||||
auth: {
|
||||
id_token: idToken,
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
},
|
||||
};
|
||||
|
||||
// update .serverlessrc
|
||||
setConfig(userConfig);
|
||||
|
||||
// identify user for better onboarding
|
||||
userStats
|
||||
.identify({
|
||||
id,
|
||||
frameworkId,
|
||||
email: decoded.email,
|
||||
// unix timestamp
|
||||
created_at: Math.round(+new Date(decoded.createdAt) / 1000),
|
||||
trackingDisabled: configuration.trackingDisabled,
|
||||
force: true,
|
||||
})
|
||||
.then(() => {
|
||||
userStats
|
||||
.track('user_loggedIn', {
|
||||
id,
|
||||
email: decoded.email,
|
||||
force: true,
|
||||
})
|
||||
.then(() => {
|
||||
// then exit process
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.serverless.cli.consoleLog(
|
||||
chalk.red('Sorry, something went wrong. Please run "serverless login" again.')
|
||||
);
|
||||
throw new this.serverless.classes.Error(
|
||||
'Failed to login due to an error. Please try again or contact hello@serverless.com'
|
||||
);
|
||||
});
|
||||
return platform.login().then(data => {
|
||||
const decoded = jwtDecode(data.idToken);
|
||||
// because platform only support github
|
||||
const id = decoded.tracking_id || decoded.sub;
|
||||
const userConfig = {
|
||||
userId: id,
|
||||
frameworkId,
|
||||
users: {},
|
||||
};
|
||||
// set user auth in global .serverlessrc file
|
||||
userConfig.users[id] = {
|
||||
userId: id,
|
||||
name: decoded.name,
|
||||
email: decoded.email,
|
||||
username: decoded.nickname,
|
||||
auth: configuration.users[id].auth,
|
||||
dashboard: data,
|
||||
};
|
||||
|
||||
fetchCliLogin();
|
||||
// update .serverlessrc
|
||||
configUtils.set(userConfig);
|
||||
|
||||
// identify user for better onboarding
|
||||
userStats
|
||||
.identify({
|
||||
id,
|
||||
frameworkId,
|
||||
email: decoded.email,
|
||||
// unix timestamp
|
||||
created_at: Math.round(+new Date(decoded.createdAt) / 1000),
|
||||
trackingDisabled: configuration.trackingDisabled,
|
||||
force: true,
|
||||
})
|
||||
.then(() => {
|
||||
userStats
|
||||
.track('user_loggedIn', {
|
||||
id,
|
||||
email: decoded.email,
|
||||
force: true,
|
||||
});
|
||||
});
|
||||
this.serverless.cli.log('You are now logged in');
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,9 +30,9 @@ class Logout {
|
||||
// that invalidate a refresh token in Auth0 (using the Auth0 Management API).
|
||||
|
||||
if (globalConfig && globalConfig.users && globalConfig.users[currentId]) {
|
||||
if (globalConfig.users[currentId].auth) {
|
||||
if (globalConfig.users[currentId].dashboard) {
|
||||
// remove auth tokens from user
|
||||
configUtils.set(`users.${currentId}.auth`, null);
|
||||
configUtils.set(`users.${currentId}.dashboard`, null);
|
||||
// log stat
|
||||
userStats.track('user_loggedOut').then(() => {
|
||||
this.serverless.cli.consoleLog('Successfully logged out.');
|
||||
|
||||
@ -2,64 +2,21 @@
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const gql = require('graphql-tag');
|
||||
const jwtDecode = require('jwt-decode');
|
||||
const BbPromise = require('bluebird');
|
||||
const fsExtra = require('../../utils/fs/fse');
|
||||
const fetch = require('node-fetch');
|
||||
const chalk = require('chalk');
|
||||
const functionInfoUtils = require('../../utils/functionInfoUtils');
|
||||
const createApolloClient = require('../../utils/createApolloClient');
|
||||
const getAuthToken = require('../../utils/getAuthToken');
|
||||
const selectServicePublish = require('../../utils/selectors/selectServicePublish');
|
||||
|
||||
// NOTE Needed for apollo to work
|
||||
global.fetch = fetch;
|
||||
|
||||
const config = {
|
||||
PLATFORM_FRONTEND_BASE_URL: 'https://platform.serverless.com/',
|
||||
GRAPHQL_ENDPOINT_URL: 'https://graphql.serverless.com/graphql',
|
||||
};
|
||||
|
||||
function addReadme(attributes, readmePath) {
|
||||
if (fs.existsSync(readmePath)) {
|
||||
const readmeContent = fsExtra.readFileSync(readmePath).toString('utf8');
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
attributes.readme = readmeContent;
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
function fetchEndpoint(provider) {
|
||||
return provider
|
||||
.request(
|
||||
'CloudFormation',
|
||||
'describeStacks',
|
||||
{ StackName: provider.naming.getStackName() },
|
||||
{ useCache: true } // Use request cache
|
||||
)
|
||||
.then(result => {
|
||||
let endpoint = null;
|
||||
|
||||
if (result) {
|
||||
result.Stacks[0].Outputs
|
||||
.filter(x => x.OutputKey.match(provider.naming.getServiceEndpointRegex()))
|
||||
.forEach(x => {
|
||||
endpoint = x.OutputValue;
|
||||
});
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
});
|
||||
}
|
||||
const crypto = require('crypto');
|
||||
const _ = require('lodash');
|
||||
const platform = require('@serverless/platform-sdk');
|
||||
const getAccessKey = require('../../utils/getAccessKey');
|
||||
|
||||
class Platform {
|
||||
constructor(serverless, options) {
|
||||
this.serverless = serverless;
|
||||
this.options = options;
|
||||
this.provider = this.serverless.getProvider('aws');
|
||||
this.config = {
|
||||
app: this.serverless.service.app,
|
||||
tenant: this.serverless.service.tenant,
|
||||
};
|
||||
// NOTE for the time being we only track services published to AWS
|
||||
if (this.provider) {
|
||||
this.hooks = {
|
||||
@ -69,141 +26,120 @@ class Platform {
|
||||
}
|
||||
}
|
||||
|
||||
archiveServiceRequest(name, client) {
|
||||
return client.mutate({
|
||||
mutation: gql`
|
||||
mutation archiveService($name: String!) {
|
||||
archiveService(name: $name) {
|
||||
archived
|
||||
publishService() {
|
||||
return getAccessKey(this.config.tenant).then(accessKey => {
|
||||
if (!accessKey || !this.config.app || !this.config.tenant) {
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
this.serverless.cli.log('Publishing service to Serverless Platform...');
|
||||
|
||||
const service = this.serverless.service;
|
||||
|
||||
const data = {
|
||||
app: this.config.app,
|
||||
tenant: this.config.tenant,
|
||||
accessKey,
|
||||
version: '0.1.0',
|
||||
service: this.getServiceData(),
|
||||
functions: [],
|
||||
subscriptions: [],
|
||||
};
|
||||
|
||||
Object.keys(service.functions).forEach(fn => {
|
||||
const fnObj = _.omit(service.functions[fn], ['events']);
|
||||
fnObj.functionId = this.serverless.service.getFunction(fn).name;
|
||||
data.functions.push(fnObj);
|
||||
|
||||
this.serverless.service.getAllEventsInFunction(fn).forEach(event => {
|
||||
const subscription = {
|
||||
functionId: this.serverless.service.getFunction(fn).name,
|
||||
};
|
||||
|
||||
// in case of sls custom event type...
|
||||
if (typeof event === 'string') {
|
||||
subscription.type = event;
|
||||
subscription.subscriptionId = crypto.createHash('md5')
|
||||
.update(`${subscription.functionId}-${event}`).digest('hex');
|
||||
// in case of aws apigateway
|
||||
} else if (Object.keys(event)[0] === 'http') {
|
||||
subscription.type = 'aws.apigateway.http';
|
||||
if (typeof event.http === 'string') {
|
||||
// todo http shortcut
|
||||
} else if (typeof event.http === 'object') {
|
||||
subscription.method = event.http.method;
|
||||
subscription.path = event.http.path;
|
||||
subscription.subscriptionId = crypto.createHash('md5')
|
||||
.update(JSON.stringify(subscription)).digest('hex');
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { name },
|
||||
// todo support aws events and http
|
||||
data.subscriptions.push(subscription);
|
||||
});
|
||||
});
|
||||
|
||||
return new BbPromise((resolve, reject) => {
|
||||
platform.publishService(data)
|
||||
.then(() => {
|
||||
this.serverless.cli
|
||||
.log('Successfully published your service on the Serverless Platform');
|
||||
resolve();
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(err => {
|
||||
this.serverless.cli.log('Failed to published your service on the Serverless Platform');
|
||||
reject(err.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getServiceData() {
|
||||
const serviceData = {
|
||||
name: this.serverless.service.service,
|
||||
stage: this.serverless.processedInput.options.stage
|
||||
|| this.serverless.service.provider.stage,
|
||||
provider: this.serverless.service.provider,
|
||||
};
|
||||
if (this.serverless.service.serviceObject.description) {
|
||||
serviceData.description = this.serverless.service.serviceObject.description;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.license) {
|
||||
serviceData.license = this.serverless.service.serviceObject.license;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.bugs) {
|
||||
serviceData.bugs = this.serverless.service.serviceObject.bugs;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.repository) {
|
||||
serviceData.repository = this.serverless.service.serviceObject.repository;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.homepage) {
|
||||
serviceData.homepage = this.serverless.service.serviceObject.homepage;
|
||||
}
|
||||
return serviceData;
|
||||
}
|
||||
|
||||
archiveService() {
|
||||
const authToken = this.getAuthToken();
|
||||
const publishFlag = selectServicePublish(this.serverless.service);
|
||||
if (!authToken || !publishFlag) {
|
||||
// NOTE archiveService is an opt-in feature and no warning is needed
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
const clientWithAuth = createApolloClient(config.GRAPHQL_ENDPOINT_URL, authToken);
|
||||
return this.archiveServiceRequest(this.serverless.service.service, clientWithAuth)
|
||||
.then(response => {
|
||||
this.serverless.cli.log('Successfully archived your service on the Serverless Platform');
|
||||
return response.data;
|
||||
})
|
||||
.catch(err => {
|
||||
this.serverless.cli.log('Failed to archived your service on the Serverless Platform');
|
||||
throw new this.serverless.classes.Error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
publishServiceRequest(service, client) {
|
||||
return client
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation publishService($service: ServicePublishInputType!) {
|
||||
publishService(service: $service) {
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { service },
|
||||
})
|
||||
.then(response => response.data);
|
||||
}
|
||||
|
||||
getAuthToken() {
|
||||
return getAuthToken();
|
||||
}
|
||||
|
||||
publishService() {
|
||||
const authToken = this.getAuthToken();
|
||||
const publishFlag = selectServicePublish(this.serverless.service);
|
||||
if (!authToken || !publishFlag) {
|
||||
// NOTE publishService is an opt-in feature and no warning is needed
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
this.serverless.cli.log('Publish service to Serverless Platform...');
|
||||
|
||||
const clientWithAuth = createApolloClient(config.GRAPHQL_ENDPOINT_URL, authToken);
|
||||
|
||||
const region = this.provider.getRegion();
|
||||
|
||||
return this.provider.getAccountInfo().then(res =>
|
||||
fetchEndpoint(this.provider).then(endpoint => {
|
||||
const funcs = this.serverless.service.getAllFunctions().map(key => {
|
||||
const arnName = functionInfoUtils.aws.getArnName(key, this.serverless);
|
||||
let funcAttributes = {
|
||||
name: key,
|
||||
runtime: functionInfoUtils.aws.getRuntime(key, this.serverless),
|
||||
memory: functionInfoUtils.aws.getMemorySize(key, this.serverless),
|
||||
timeout: functionInfoUtils.aws.getTimeout(key, this.serverless),
|
||||
provider: this.serverless.service.provider.name,
|
||||
originId: `arn:${res.partition}:lambda:${region}:${res.accountId}:function:${arnName}`,
|
||||
endpoints: functionInfoUtils.aws.getEndpoints(key, this.serverless, endpoint),
|
||||
};
|
||||
if (this.serverless.service.functions[key].readme) {
|
||||
funcAttributes = addReadme(
|
||||
funcAttributes,
|
||||
this.serverless.service.functions[key].readme
|
||||
);
|
||||
}
|
||||
if (this.serverless.service.functions[key].description) {
|
||||
funcAttributes.description = this.serverless.service.functions[key].description;
|
||||
}
|
||||
return funcAttributes;
|
||||
return getAccessKey(this.config.tenant).then(accessKey => {
|
||||
if (!accessKey || !this.config.app || !this.config.tenant) {
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
const data = {
|
||||
name: this.serverless.service.service,
|
||||
tenant: this.config.tenant,
|
||||
app: this.config.app,
|
||||
accessKey,
|
||||
};
|
||||
return platform.archiveService(data)
|
||||
.then(() => {
|
||||
this.serverless.cli.log('Successfully archived your service on the Serverless Platform');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(err => {
|
||||
this.serverless.cli.log('Failed to archived your service on the Serverless Platform');
|
||||
throw new this.serverless.classes.Error(err.message);
|
||||
});
|
||||
const serviceData = {
|
||||
name: this.serverless.service.service,
|
||||
stage: this.serverless.processedInput.options.stage,
|
||||
functions: funcs,
|
||||
};
|
||||
if (this.serverless.service.serviceObject.description) {
|
||||
serviceData.description = this.serverless.service.serviceObject.description;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.license) {
|
||||
serviceData.license = this.serverless.service.serviceObject.license;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.bugs) {
|
||||
serviceData.bugs = this.serverless.service.serviceObject.bugs;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.repository) {
|
||||
serviceData.repository = this.serverless.service.serviceObject.repository;
|
||||
}
|
||||
if (this.serverless.service.serviceObject.homepage) {
|
||||
serviceData.homepage = this.serverless.service.serviceObject.homepage;
|
||||
}
|
||||
|
||||
// NOTE can be improved by making sure it captures multiple variations
|
||||
// of readme file name e.g. readme.md, readme.txt, Readme.md
|
||||
const readmePath = path.join(this.serverless.config.servicePath, 'README.md');
|
||||
const serviceDataWithReadme = addReadme(serviceData, readmePath);
|
||||
|
||||
return new BbPromise((resolve, reject) => {
|
||||
this.publishServiceRequest(serviceDataWithReadme, clientWithAuth)
|
||||
.then(() => {
|
||||
const username = jwtDecode(authToken).nickname;
|
||||
const serviceName = this.serverless.service.service;
|
||||
const url = `${config.PLATFORM_FRONTEND_BASE_URL}services/${username}/${serviceName}`;
|
||||
console.log('Service successfully published! Your service details are available at:');
|
||||
console.log(chalk.green(url));
|
||||
resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
this.serverless.cli.log(
|
||||
"Couldn't publish this deploy information to the Serverless Platform."
|
||||
);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
const apollo = require('apollo-client');
|
||||
|
||||
module.exports = (endpoint, auth0IdToken) => {
|
||||
const networkInterface = apollo.createNetworkInterface({ uri: endpoint });
|
||||
|
||||
if (auth0IdToken) {
|
||||
networkInterface.use([{
|
||||
applyMiddleware(req, next) {
|
||||
if (!req.options.headers) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
req.options.headers = {};
|
||||
}
|
||||
const token = auth0IdToken;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
req.options.headers.authorization = token ? `Bearer ${token}` : null;
|
||||
next();
|
||||
},
|
||||
}]);
|
||||
}
|
||||
|
||||
return new apollo.ApolloClient({
|
||||
networkInterface,
|
||||
});
|
||||
};
|
||||
39
lib/utils/getAccessKey.js
Normal file
39
lib/utils/getAccessKey.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
const configUtils = require('./config');
|
||||
const { createAccessKey } = require('@serverless/platform-sdk');
|
||||
const BbPromise = require('bluebird');
|
||||
|
||||
function getAccessKey(tenant) {
|
||||
if (process.env.SERVERLESS_ACCESS_KEY) {
|
||||
return BbPromise.resolve(process.env.SERVERLESS_ACCESS_KEY);
|
||||
}
|
||||
if (!tenant) {
|
||||
return BbPromise.resolve(null);
|
||||
}
|
||||
|
||||
const userConfig = configUtils.getConfig();
|
||||
const currentId = userConfig.userId;
|
||||
const globalConfig = configUtils.getGlobalConfig();
|
||||
if (globalConfig.users && globalConfig.users[currentId] &&
|
||||
globalConfig.users[currentId].dashboard) {
|
||||
if (globalConfig.users[currentId].dashboard.accessKey) {
|
||||
return BbPromise.resolve(globalConfig.users[currentId].dashboard.accessKey);
|
||||
} else if (globalConfig.users[currentId].dashboard.idToken) {
|
||||
const data = {
|
||||
tenant,
|
||||
username: globalConfig.users[currentId].username,
|
||||
idToken: globalConfig.users[currentId].dashboard.idToken,
|
||||
title: 'Framework',
|
||||
};
|
||||
return createAccessKey(data).then(res => res.json()).then(res => {
|
||||
globalConfig.users[currentId].dashboard.accessKey = res.secretAccessKey;
|
||||
configUtils.set(globalConfig);
|
||||
return res.secretAccessKey;
|
||||
});
|
||||
}
|
||||
}
|
||||
return BbPromise.resolve(null);
|
||||
}
|
||||
|
||||
module.exports = getAccessKey;
|
||||
@ -1,31 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const opn = require('opn');
|
||||
const chalk = require('chalk');
|
||||
const isDockerContainer = require('is-docker');
|
||||
|
||||
function displayManualOpenMessage(url, err) {
|
||||
// https://github.com/sindresorhus/log-symbols
|
||||
console.log('---------------------------');
|
||||
const errMsg = err ? `\nError: ${err.message}` : '';
|
||||
const msg = `Unable to open browser automatically${errMsg}`;
|
||||
console.log(`🙈 ${chalk.red(msg)}`);
|
||||
console.log(chalk.green('Please open your browser & open the URL below to login:'));
|
||||
console.log(chalk.yellow(url));
|
||||
console.log('---------------------------');
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = function openBrowser(url) {
|
||||
let browser = process.env.BROWSER;
|
||||
if (browser === 'none' || isDockerContainer()) {
|
||||
return displayManualOpenMessage(url);
|
||||
}
|
||||
if (process.platform === 'darwin' && browser === 'open') {
|
||||
browser = undefined;
|
||||
}
|
||||
const options = { app: browser };
|
||||
return opn(url, options).catch(err => displayManualOpenMessage(url, err));
|
||||
};
|
||||
@ -1,37 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const proxyquire = require('proxyquire');
|
||||
const sinon = require('sinon');
|
||||
const expect = require('chai').expect;
|
||||
|
||||
describe('#openBrowser', () => {
|
||||
let openBrowser;
|
||||
let opnStub;
|
||||
let isDockerStub;
|
||||
|
||||
beforeEach(() => {
|
||||
opnStub = sinon.stub().resolves({});
|
||||
opnStub = sinon.stub().resolves({});
|
||||
isDockerStub = sinon.stub().returns(false);
|
||||
|
||||
openBrowser = proxyquire('./openBrowser', {
|
||||
opn: opnStub,
|
||||
'is-docker': isDockerStub,
|
||||
});
|
||||
});
|
||||
|
||||
it('should open the browser with the provided url', () => {
|
||||
openBrowser('http://www.example.com');
|
||||
expect(opnStub.getCall(0).args[0]).to.equal('http://www.example.com');
|
||||
});
|
||||
|
||||
it('should open the browser with the provided url', () => {
|
||||
isDockerStub = sinon.stub().returns(true);
|
||||
openBrowser = proxyquire('./openBrowser', {
|
||||
opn: opnStub,
|
||||
'is-docker': isDockerStub,
|
||||
});
|
||||
openBrowser('http://www.example.com');
|
||||
expect(opnStub.notCalled).to.equal(true);
|
||||
});
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
const selectServicePublish = (service) => _.get(service, 'serviceObject.publish', true);
|
||||
|
||||
module.exports = selectServicePublish;
|
||||
@ -1,18 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const expect = require('chai').expect;
|
||||
const selectServicePublish = require('./selectServicePublish');
|
||||
|
||||
describe('#selectServicePublish()', () => {
|
||||
it('should return the publish value of the service object', () => {
|
||||
const service = {
|
||||
serviceObject: {
|
||||
publish: true,
|
||||
},
|
||||
};
|
||||
|
||||
const result = selectServicePublish(service);
|
||||
|
||||
expect(result).to.equal(true);
|
||||
});
|
||||
});
|
||||
@ -92,7 +92,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@serverless/fdk": "^0.5.1",
|
||||
"apollo-client": "^1.9.2",
|
||||
"@serverless/platform-sdk": "file:../platform-sdk",
|
||||
"archiver": "^1.1.0",
|
||||
"async": "^1.5.2",
|
||||
"aws-sdk": "^2.228.0",
|
||||
@ -106,8 +106,6 @@
|
||||
"get-stdin": "^5.0.1",
|
||||
"globby": "^6.1.0",
|
||||
"graceful-fs": "^4.1.11",
|
||||
"graphql": "^0.10.1",
|
||||
"graphql-tag": "^2.4.0",
|
||||
"https-proxy-agent": "^2.2.1",
|
||||
"is-docker": "^1.1.0",
|
||||
"js-yaml": "^3.6.1",
|
||||
@ -118,9 +116,7 @@
|
||||
"minimist": "^1.2.0",
|
||||
"moment": "^2.13.0",
|
||||
"node-fetch": "^1.6.0",
|
||||
"node-forge": "^0.7.1",
|
||||
"object-hash": "^1.2.0",
|
||||
"opn": "^5.0.0",
|
||||
"promise-queue": "^2.2.3",
|
||||
"raven": "^1.2.1",
|
||||
"rc": "^1.1.6",
|
||||
|
||||
6
test/.gitignore
vendored
Normal file
6
test/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# package directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
16
test/handler.js
Normal file
16
test/handler.js
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
module.exports.hello = (event, context, callback) => {
|
||||
const response = {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({
|
||||
message: 'Go Serverless v1.0! Your function executed successfully!',
|
||||
input: event,
|
||||
}),
|
||||
};
|
||||
|
||||
callback(null, response);
|
||||
|
||||
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
|
||||
// callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
|
||||
};
|
||||
18
test/serverless.yml
Normal file
18
test/serverless.yml
Normal file
@ -0,0 +1,18 @@
|
||||
service: eslam-serverless-dashboard-demo
|
||||
|
||||
tenant: eahefnawy
|
||||
app: framework
|
||||
|
||||
provider:
|
||||
name: aws
|
||||
runtime: nodejs6.10
|
||||
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
events:
|
||||
- user.created
|
||||
- http:
|
||||
path: test-again
|
||||
method: post
|
||||
cors: true
|
||||
Loading…
x
Reference in New Issue
Block a user