DB migrations

Google button
This commit is contained in:
Tom Moor
2018-05-28 20:31:53 -07:00
parent ddd2b82d20
commit 72d874444e
12 changed files with 107 additions and 31 deletions

View File

@ -16,8 +16,10 @@ let authenticatedStores;
const Auth = ({ children }: Props) => {
if (stores.auth.authenticated) {
stores.auth.fetch();
// TODO: Show loading state
if (!stores.auth.team || !stores.auth.user) {
stores.auth.fetch();
return null;
}

View File

@ -14,7 +14,9 @@ router.post('hooks.unfurl', async ctx => {
throw new AuthenticationError('Invalid token');
// TODO: Everything from here onwards will get moved to an async job
const user = await User.find({ where: { slackId: event.user } });
const user = await User.find({
where: { service: 'slack', serviceId: event.user },
});
if (!user) return;
const auth = await Authentication.find({
@ -55,7 +57,8 @@ router.post('hooks.slack', async ctx => {
const user = await User.find({
where: {
slackId: user_id,
service: 'slack',
serviceId: user_id,
},
});

View File

@ -32,7 +32,7 @@ describe('#hooks.unfurl', async () => {
event: {
type: 'link_shared',
channel: 'Cxxxxxx',
user: user.slackId,
user: user.serviceId,
message_ts: '123456789.9875',
links: [
{
@ -55,7 +55,7 @@ describe('#hooks.slack', async () => {
const res = await server.post('/api/hooks.slack', {
body: {
token: process.env.SLACK_VERIFICATION_TOKEN,
user_id: user.slackId,
user_id: user.serviceId,
text: 'dsfkndfskndsfkn',
},
});
@ -70,7 +70,7 @@ describe('#hooks.slack', async () => {
const res = await server.post('/api/hooks.slack', {
body: {
token: process.env.SLACK_VERIFICATION_TOKEN,
user_id: user.slackId,
user_id: user.serviceId,
text: document.title,
},
});
@ -98,7 +98,7 @@ describe('#hooks.slack', async () => {
const res = await server.post('/api/hooks.slack', {
body: {
token: 'wrong-verification-token',
user_id: user.slackId,
user_id: user.serviceId,
text: 'Welcome',
},
});

View File

@ -1,6 +1,7 @@
// @flow
import Router from 'koa-router';
import addMonths from 'date-fns/add_months';
import { capitalize } from 'lodash';
import { OAuth2Client } from 'google-auth-library';
import { User, Team } from '../models';
@ -32,17 +33,14 @@ router.get('google.callback', async ctx => {
const response = await client.getToken(code);
client.setCredentials(response.tokens);
console.log('Tokens acquired.');
console.log(response.tokens);
const profile = await client.request({
url: 'https://www.googleapis.com/oauth2/v1/userinfo',
});
const teamName = profile.data.hd.split('.')[0];
const teamName = capitalize(profile.data.hd.split('.')[0]);
const [team, isFirstUser] = await Team.findOrCreate({
where: {
slackId: profile.data.hd,
googleId: profile.data.hd,
},
defaults: {
name: teamName,
@ -50,9 +48,10 @@ router.get('google.callback', async ctx => {
},
});
const [user, isFirstSignin] = await User.findOrCreate({
const [user] = await User.findOrCreate({
where: {
slackId: profile.data.id,
service: 'google',
serviceId: profile.data.id,
teamId: team.id,
},
defaults: {
@ -63,10 +62,6 @@ router.get('google.callback', async ctx => {
},
});
if (!isFirstSignin) {
await user.save();
}
if (isFirstUser) {
await team.createFirstCollection(user.id);
}
@ -77,7 +72,7 @@ router.get('google.callback', async ctx => {
});
ctx.cookies.set('accessToken', user.getJwtToken(), {
httpOnly: false,
expires: addMonths(new Date(), 6),
expires: addMonths(new Date(), 1),
});
ctx.redirect('/');

View File

@ -26,7 +26,9 @@ router.post('auth.slack', async ctx => {
const data = await Slack.oauthAccess(code);
let user = await User.findOne({ where: { slackId: data.user.id } });
let user = await User.findOne({
where: { service: 'slack', serviceId: data.user.id },
});
let team = await Team.findOne({ where: { slackId: data.team.id } });
const isFirstUser = !team;
@ -48,7 +50,8 @@ router.post('auth.slack', async ctx => {
await user.save();
} else {
user = await User.create({
slackId: data.user.id,
service: 'slack',
serviceId: data.user.id,
name: data.user.name,
email: data.user.email,
teamId: team.id,

View File

@ -0,0 +1,27 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('teams', 'googleId', {
type: Sequelize.STRING,
allowNull: true,
unique: true
});
await queryInterface.addColumn('teams', 'avatarUrl', {
type: Sequelize.STRING,
allowNull: true,
});
await queryInterface.addColumn('users', 'service', {
type: Sequelize.STRING,
allowNull: true,
defaultValue: 'slack'
});
await queryInterface.renameColumn('users', 'slackId', 'serviceId');
await queryInterface.addIndex('teams', ['googleId']);
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('teams', 'googleId');
await queryInterface.removeColumn('teams', 'avatarUrl');
await queryInterface.removeColumn('users', 'service');
await queryInterface.renameColumn('users', 'serviceId', 'slackId');
await queryInterface.removeIndex('teams', ['googleId']);
}
}

View File

@ -13,6 +13,8 @@ const Team = sequelize.define(
},
name: DataTypes.STRING,
slackId: { type: DataTypes.STRING, allowNull: true },
googleId: { type: DataTypes.STRING, allowNull: true },
avatarUrl: { type: DataTypes.STRING, allowNull: true },
slackData: DataTypes.JSONB,
},
{

View File

@ -25,7 +25,8 @@ const User = sequelize.define(
passwordDigest: DataTypes.STRING,
isAdmin: DataTypes.BOOLEAN,
slackAccessToken: encryptedFields.vault('slackAccessToken'),
slackId: { type: DataTypes.STRING, allowNull: true, unique: true },
service: { type: DataTypes.STRING, allowNull: true, unique: true },
serviceId: { type: DataTypes.STRING, allowNull: true, unique: true },
slackData: DataTypes.JSONB,
jwtSecret: encryptedFields.vault('jwtSecret'),
suspendedAt: DataTypes.DATE,

View File

@ -3,6 +3,7 @@ import * as React from 'react';
import styled from 'styled-components';
import { signin } from '../../../shared/utils/routeHelpers';
import Flex from '../../../shared/components/Flex';
import GoogleLogo from '../../../shared/components/GoogleLogo';
import SlackLogo from '../../../shared/components/SlackLogo';
import { color } from '../../../shared/styles/constants';
@ -10,22 +11,27 @@ type Props = {
lastLoggedIn: string,
};
const SlackSignin = ({ lastLoggedIn }: Props) => {
const SignupButton = ({ lastLoggedIn }: Props) => {
return (
<Flex justify="center">
<Flex>
<Flex column>
<Button href={signin('slack')}>
<SlackLogo />
<Spacer>Sign In with Slack</Spacer>
</Button>
{lastLoggedIn === 'slack' && 'You signed in with Slack previously'}
<LastLogin>
{lastLoggedIn === 'slack' && 'You signed in with Slack previously'}
</LastLogin>
</Flex>
&nbsp;
<Flex>
<Flex column>
<Button href={signin('google')}>
<GoogleLogo />
<Spacer>Sign In with Google</Spacer>
</Button>
{lastLoggedIn === 'google' && 'You signed in with Google previously'}
<LastLogin>
{lastLoggedIn === 'google' && 'You signed in with Google previously'}
</LastLogin>
</Flex>
</Flex>
);
@ -43,6 +49,13 @@ const Button = styled.a`
background: ${color.black};
border-radius: 4px;
font-weight: 600;
height: 56px;
`;
export default SlackSignin;
const LastLogin = styled.p`
font-size: 12px;
color: ${color.slate};
padding-top: 4px;
`;
export default SignupButton;

View File

@ -40,7 +40,8 @@ export async function buildUser(overrides: Object = {}) {
username: `user${count}`,
name: `User ${count}`,
password: 'test123!',
slackId: uuid.v4(),
service: 'slack',
serviceId: uuid.v4(),
...overrides,
});
}

View File

@ -30,7 +30,8 @@ const seed = async () => {
name: 'User 1',
password: 'test123!',
teamId: team.id,
slackId: 'U2399UF2P',
service: 'slack',
serviceId: 'U2399UF2P',
slackData: {
id: 'U2399UF2P',
image_192: 'http://example.com/avatar.png',
@ -45,7 +46,8 @@ const seed = async () => {
password: 'test123!',
teamId: team.id,
isAdmin: true,
slackId: 'U2399UF1P',
service: 'slack',
serviceId: 'U2399UF1P',
slackData: {
id: 'U2399UF1P',
image_192: 'http://example.com/avatar.png',

View File

@ -0,0 +1,27 @@
// @flow
import * as React from 'react';
type Props = {
size?: number,
fill?: string,
className?: string,
};
function GoogleLogo({ size = 34, fill = '#FFF', className }: Props) {
return (
<svg
fill={fill}
width={size}
height={size}
viewBox="0 0 34 34"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<g>
<path d="M32.6162791,13.9090909 L16.8837209,13.9090909 L16.8837209,20.4772727 L25.9395349,20.4772727 C25.0953488,24.65 21.5651163,27.0454545 16.8837209,27.0454545 C11.3581395,27.0454545 6.90697674,22.5636364 6.90697674,17 C6.90697674,11.4363636 11.3581395,6.95454545 16.8837209,6.95454545 C19.2627907,6.95454545 21.4116279,7.80454545 23.1,9.19545455 L28.0116279,4.25 C25.0186047,1.62272727 21.1813953,0 16.8837209,0 C7.52093023,0 0,7.57272727 0,17 C0,26.4272727 7.52093023,34 16.8837209,34 C25.3255814,34 33,27.8181818 33,17 C33,15.9954545 32.8465116,14.9136364 32.6162791,13.9090909 Z" />
</g>
</svg>
);
}
export default GoogleLogo;