Skip to content

Commit

Permalink
Add database to track installations.
Browse files Browse the repository at this point in the history
  • Loading branch information
adunkman committed Sep 1, 2021
1 parent 00ac5ea commit d6652f2
Show file tree
Hide file tree
Showing 10 changed files with 747 additions and 16 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ SLACK_SIGNING_SECRET=a1A2a1A2a1A2a1A2a1A2a1A2a1A2a1A2
SLACK_CLIENT_ID=1111111111111.2222222222222
SLACK_CLIENT_SECRET=b1B2b1B2b1B2b1B2b1B2b1B2b1B2b1B2
APP_STATE_SECRET=my-state-secret
LOCAL_POSTGRES_PASSWORD=local-development-password
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: Test

on: push

env:
LOCAL_POSTGRES_PASSWORD: ci-password

jobs:
test:
name: Test
Expand Down
7 changes: 7 additions & 0 deletions .sequelizerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const path = require("path");

module.exports = {
"url": process.env.DATABASE_URL,
"migrations-path": path.resolve("migrations"),
"models-path": path.resolve("bot", "models"),
}
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
FROM node:16-alpine3.14
WORKDIR /app

ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz

ADD ./package.json ./yarn.lock ./
RUN yarn install

ENTRYPOINT ["yarn"]
ENTRYPOINT ["dockerize", "-wait", "tcp://db:5432", "yarn"]
CMD ["start"]
38 changes: 34 additions & 4 deletions bot/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { App, ExpressReceiver, LogLevel, subtype } from "@slack/bolt";
import { InstallProvider } from "@slack/oauth";
import { App, ExpressReceiver, Installation as InstallationType, LogLevel, subtype } from "@slack/bolt";
import { Sequelize } from "sequelize";
import { InstallationFactory } from "./models/installation";
import { CLOSE_MESSAGE_ACTION_ID, handleMessageClose } from "./actions/closeMessage";
import { handleMenuClick, OVERFLOW_MENU_CLICK_ACTION_ID } from "./actions/menuClick";
import { handleMessage } from "./actions/message";
Expand All @@ -8,21 +9,49 @@ import { manifest } from "./manifest/manifest";
import { config } from "./triggers/triggers";

const {
SLACK_TOKEN,
SLACK_SIGNING_SECRET,
SLACK_CLIENT_ID,
SLACK_CLIENT_SECRET,
APP_STATE_SECRET,
DATABASE_URL,
PORT = "3000",
} = process.env;

const sequelize = new Sequelize(DATABASE_URL);
const Installation = InstallationFactory(sequelize);

const receiver = new ExpressReceiver({
signingSecret: SLACK_SIGNING_SECRET,
clientId: SLACK_CLIENT_ID,
clientSecret: SLACK_CLIENT_SECRET,
stateSecret: APP_STATE_SECRET,
scopes: manifest.oauth_config.scopes.bot,
logLevel: LogLevel.INFO,
installationStore: {
storeInstallation: async (installation) => {
await Installation.create({
id: installation.isEnterpriseInstall
? installation.enterprise.id
: installation.team.id,
isEnterpriseInstallation: installation.isEnterpriseInstall,
installationObject: installation
});
},
fetchInstallation: async (query) => {
const installation = await Installation.findByPk(
query.isEnterpriseInstall ? query.enterpriseId : query.teamId
);

return installation.installationObject as InstallationType;
},
deleteInstallation: async (query) => {
const installation = await Installation.findByPk(
query.isEnterpriseInstall ? query.enterpriseId : query.teamId
);

await installation.destroy();
}
}
});

// Trust and use Heroku’s X-Forwarded-* headers.
Expand All @@ -31,6 +60,7 @@ receiver.app.set("trust proxy", true);
const app = new App({ receiver });

(async () => {
await sequelize.authenticate();
await app.start(+PORT);

app.message(config.allTriggersRegExp, handleMessage);
Expand All @@ -49,5 +79,5 @@ const app = new App({ receiver });
scopes: manifest.oauth_config.scopes.bot,
redirectUri: `${req.protocol}://${req.hostname}/slack/oauth_redirect`,
}));
})
});
})();
53 changes: 53 additions & 0 deletions bot/models/installation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { DataTypes, Model, Sequelize } from "sequelize";

export interface InstallationCreationAttributes {
id: string;
isEnterpriseInstallation: boolean;
installationObject: object;
}

export interface InstallationAttributes {
id: string;
isEnterpriseInstallation: boolean;
installationObject: object;
createdAt: Date;
updatedAt: Date;
}

export class Installation extends Model<InstallationAttributes, InstallationCreationAttributes> implements InstallationAttributes {
id: string;
isEnterpriseInstallation: boolean;
installationObject: object;
createdAt: Date;
updatedAt: Date;
}

export function InstallationFactory(sequelize: Sequelize) {
Installation.init({
id: {
allowNull: false,
primaryKey: true,
type: DataTypes.STRING,
},
isEnterpriseInstallation: {
allowNull: false,
type: DataTypes.BOOLEAN,
},
installationObject: {
allowNull: false,
type: DataTypes.JSON,
},
createdAt: {
allowNull: false,
type: DataTypes.DATE,
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE,
}
}, {
sequelize,
});

return Installation;
};
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,25 @@ services:
volumes:
- ./:/app
- /app/node_modules/
depends_on:
- db
ports:
- "3000:3000"
environment:
SLACK_SIGNING_SECRET: $SLACK_SIGNING_SECRET
SLACK_CLIENT_ID: $SLACK_CLIENT_ID
SLACK_CLIENT_SECRET: $SLACK_CLIENT_SECRET
APP_STATE_SECRET: $APP_STATE_SECRET
DATABASE_URL: postgres://postgres:${LOCAL_POSTGRES_PASSWORD}@db:5432/postgres

adminer:
image: adminer:4.8.1-standalone
depends_on:
- db
ports:
- "8080:8080"

db:
image: postgres:13.4-alpine3.14
environment:
POSTGRES_PASSWORD: $LOCAL_POSTGRES_PASSWORD
31 changes: 31 additions & 0 deletions migrations/20210901042233-create-installation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Installations', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.STRING
},
isEnterpriseInstallation: {
allowNull: false,
type: Sequelize.BOOLEAN
},
installationObject: {
allowNull: false,
type: Sequelize.JSON
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Installations');
}
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@
"yarn": "1.x"
},
"scripts": {
"start": "ts-node bot/app.ts",
"start": "yarn run sequelize-cli db:migrate && ts-node bot/app.ts",
"test": "jest"
},
"dependencies": {
"@slack/bolt": "^3.6.0",
"@types/js-yaml": "^4.0.3",
"@types/node": "^16.7.4",
"js-yaml": "^4.1.0",
"pg": "^8.7.1",
"pg-hstore": "^2.3.4",
"sequelize": "^6.6.5",
"sequelize-cli": "^6.2.0",
"ts-node": "^10.2.1",
"typescript": "^4.4.2"
},
Expand Down
Loading

0 comments on commit d6652f2

Please sign in to comment.