diff --git a/Server/.eslintrc.js b/Server/.eslintrc.js index a5b82de..b0a06a6 100644 --- a/Server/.eslintrc.js +++ b/Server/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - "extends": "standard" -}; \ No newline at end of file + extends: 'standard', +}; diff --git a/Server/.prettierrc.js b/Server/.prettierrc.js new file mode 100644 index 0000000..d58c0ce --- /dev/null +++ b/Server/.prettierrc.js @@ -0,0 +1,9 @@ +module.exports = { + printWidth: 90, + useTabs: false, + tabWidth: 2, + singleQuote: true, + trailingComma: 'es5', + bracketSpacing: true, + semi: true, +}; diff --git a/Server/Roleypoly.js b/Server/Roleypoly.js index 9be93fe..4856a79 100644 --- a/Server/Roleypoly.js +++ b/Server/Roleypoly.js @@ -1,36 +1,38 @@ -const log = new (require('./logger'))('Roleypoly') -const Sequelize = require('sequelize') -const fetchModels = require('./models') -const fetchApis = require('./api') +const log = new (require('./logger'))('Roleypoly'); +const Sequelize = require('sequelize'); +const fetchModels = require('./models'); +const fetchApis = require('./api'); class Roleypoly { - constructor (router, io, app) { - this.router = router - this.io = io - this.ctx = {} + constructor(router, io, app) { + this.router = router; + this.io = io; + this.ctx = {}; this.ctx.config = { - appUrl: process.env.APP_URL - } + appUrl: process.env.APP_URL, + }; - this.ctx.io = io - this.__app = app + this.ctx.io = io; + this.__app = app; - if (log.debugOn) log.warn('debug mode is on') + if (log.debugOn) log.warn('debug mode is on'); - this.__initialized = this._mountServices() + this.__initialized = this._mountServices(); } - async awaitServices () { - await this.__initialized + async awaitServices() { + await this.__initialized; } - async _mountServices () { - const sequelize = new Sequelize(process.env.DB_URL, { logging: log.sql.bind(log, log) }) - this.ctx.sql = sequelize - this.M = fetchModels(sequelize) - this.ctx.M = this.M - await sequelize.sync() + async _mountServices() { + const sequelize = new Sequelize(process.env.DB_URL, { + logging: log.sql.bind(log, log), + }); + this.ctx.sql = sequelize; + this.M = fetchModels(sequelize); + this.ctx.M = this.M; + await sequelize.sync(); // this.ctx.redis = new (require('ioredis'))({ // port: process.env.REDIS_PORT || '6379', @@ -40,16 +42,16 @@ class Roleypoly { // enableReadyCheck: true, // enableOfflineQueue: true // }) - this.ctx.server = new (require('./services/server'))(this.ctx) - this.ctx.discord = new (require('./services/discord'))(this.ctx) - this.ctx.sessions = new (require('./services/sessions'))(this.ctx) - this.ctx.P = new (require('./services/presentation'))(this.ctx) + this.ctx.server = new (require('./services/server'))(this.ctx); + this.ctx.discord = new (require('./services/discord'))(this.ctx); + this.ctx.sessions = new (require('./services/sessions'))(this.ctx); + this.ctx.P = new (require('./services/presentation'))(this.ctx); } - async mountRoutes () { - fetchApis(this.router, this.ctx) - this.__app.use(this.router.middleware()) + async mountRoutes() { + fetchApis(this.router, this.ctx); + this.__app.use(this.router.middleware()); } } -module.exports = Roleypoly +module.exports = Roleypoly; diff --git a/Server/api/auth.js b/Server/api/auth.js index 71e13f8..f163c42 100644 --- a/Server/api/auth.js +++ b/Server/api/auth.js @@ -1,77 +1,76 @@ module.exports = (R, $) => { - R.post('/api/auth/token', async (ctx) => { - const { token } = ctx.request.body + R.post('/api/auth/token', async ctx => { + const { token } = ctx.request.body; if (token == null || token === '') { - ctx.body = { err: 'token_missing' } - ctx.status = 400 - return + ctx.body = { err: 'token_missing' }; + ctx.status = 400; + return; } if (ctx.session.accessToken === undefined || ctx.session.expiresAt < Date.now()) { - const data = await $.discord.getAuthToken(token) - ctx.session.accessToken = data.access_token - ctx.session.refreshToken = data.refresh_token - ctx.session.expiresAt = Date.now() + (ctx.expires_in || 1000 * 60 * 60 * 24) + const data = await $.discord.getAuthToken(token); + ctx.session.accessToken = data.access_token; + ctx.session.refreshToken = data.refresh_token; + ctx.session.expiresAt = Date.now() + (ctx.expires_in || 1000 * 60 * 60 * 24); } - const user = await $.discord.getUser(ctx.session.accessToken) - ctx.session.userId = user.id - ctx.session.avatarHash = user.avatar + const user = await $.discord.getUser(ctx.session.accessToken); + ctx.session.userId = user.id; + ctx.session.avatarHash = user.avatar; ctx.body = { id: user.id, avatar: user.avatar, username: user.username, - discriminator: user.discriminator - } - }) + discriminator: user.discriminator, + }; + }); R.get('/api/auth/user', async ctx => { if (ctx.session.accessToken === undefined) { - ctx.body = { err: 'not_logged_in' } - ctx.status = 401 - return + ctx.body = { err: 'not_logged_in' }; + ctx.status = 401; + return; } - const user = await $.discord.getUser(ctx.session.accessToken) - ctx.session.userId = user.id - ctx.session.avatarHash = user.avatar + const user = await $.discord.getUser(ctx.session.accessToken); + ctx.session.userId = user.id; + ctx.session.avatarHash = user.avatar; ctx.body = { id: user.id, avatar: user.avatar, username: user.username, - discriminator: user.discriminator - } - }) + discriminator: user.discriminator, + }; + }); R.get('/api/auth/redirect', ctx => { - const url = $.discord.getAuthUrl() + const url = $.discord.getAuthUrl(); if (ctx.query.url === '✔️') { - ctx.body = { url } - return + ctx.body = { url }; + return; } - ctx.redirect(url) - }) + ctx.redirect(url); + }); R.post('/api/auth/logout', ctx => { - ctx.session = null - }) + ctx.session = null; + }); R.get('/api/oauth/bot', ctx => { - const url = $.discord.getBotJoinUrl() + const url = $.discord.getBotJoinUrl(); if (ctx.query.url === '✔️') { - ctx.body = { url } - return + ctx.body = { url }; + return; } - ctx.redirect(url) - }) - + ctx.redirect(url); + }); R.get('/api/oauth/bot/callback', ctx => { - console.log(ctx.request) - }) -} + console.log(ctx.request); + }); +}; diff --git a/Server/api/index.js b/Server/api/index.js index ca717b0..139f044 100644 --- a/Server/api/index.js +++ b/Server/api/index.js @@ -1,22 +1,22 @@ -const log = new (require('../logger'))('api/index') -const glob = require('glob') +const log = new (require('../logger'))('api/index'); +const glob = require('glob'); -const PROD = process.env.NODE_ENV === 'production' +const PROD = process.env.NODE_ENV === 'production'; module.exports = async (router, ctx) => { - const apis = glob.sync(`./api/**/!(index).js`) - log.debug('found apis', apis) + const apis = glob.sync(`./api/**/!(index).js`); + log.debug('found apis', apis); for (let a of apis) { if (a.endsWith('_test.js') && PROD) { - log.debug(`skipping ${a}`) - continue + log.debug(`skipping ${a}`); + continue; } - log.debug(`mounting ${a}`) + log.debug(`mounting ${a}`); try { - require(a.replace('api/', ''))(router, ctx) + require(a.replace('api/', ''))(router, ctx); } catch (e) { - log.error(`couldn't mount ${a}`, e) + log.error(`couldn't mount ${a}`, e); } } -} +}; diff --git a/Server/api/servers.js b/Server/api/servers.js index 3861c0a..384ff18 100644 --- a/Server/api/servers.js +++ b/Server/api/servers.js @@ -1,103 +1,108 @@ module.exports = (R, $) => { - R.get('/api/servers', async (ctx) => { + R.get('/api/servers', async ctx => { try { - const { userId } = ctx.session - const srv = $.discord.getRelevantServers(userId) - const presentable = await $.P.presentableServers(srv, userId) + const { userId } = ctx.session; + const srv = $.discord.getRelevantServers(userId); + const presentable = await $.P.presentableServers(srv, userId); - ctx.body = presentable + ctx.body = presentable; } catch (e) { - console.error(e.trace || e.stack) + console.error(e.trace || e.stack); } - }) + }); - R.get('/api/server/:id', async (ctx) => { - const { userId } = ctx.session - const { id } = ctx.params + R.get('/api/server/:id', async ctx => { + const { userId } = ctx.session; + const { id } = ctx.params; - const srv = $.discord.client.guilds.get(id) + const srv = $.discord.client.guilds.get(id); if (srv == null) { - ctx.body = { err: 'not found' } - ctx.status = 404 - return + ctx.body = { err: 'not found' }; + ctx.status = 404; + return; } - let gm + let gm; if (srv.members.has(userId)) { - gm = $.discord.gm(id, userId) + gm = $.discord.gm(id, userId); } else if ($.discord.isRoot(userId)) { - gm = $.discord.fakeGm({ id: userId }) + gm = $.discord.fakeGm({ id: userId }); } else { - ctx.body = { err: 'not_a_member' } - ctx.status = 400 - return + ctx.body = { err: 'not_a_member' }; + ctx.status = 400; + return; } - const server = await $.P.presentableServer(srv, gm) + const server = await $.P.presentableServer(srv, gm); - ctx.body = server - }) + ctx.body = server; + }); - R.get('/api/server/:id/slug', async (ctx) => { - const { userId } = ctx.session - const { id } = ctx.params + R.get('/api/server/:id/slug', async ctx => { + const { userId } = ctx.session; + const { id } = ctx.params; - const srv = $.discord.client.guilds.get(id) + const srv = $.discord.client.guilds.get(id); - console.log(srv) + console.log(srv); if (srv == null) { - ctx.body = { err: 'not found' } - ctx.status = 404 - return + ctx.body = { err: 'not found' }; + ctx.status = 404; + return; } - ctx.body = await $.P.serverSlug(srv) - }) + ctx.body = await $.P.serverSlug(srv); + }); - R.patch('/api/server/:id', async (ctx) => { - const { userId } = ctx.session - const { id } = ctx.params + R.patch('/api/server/:id', async ctx => { + const { userId } = ctx.session; + const { id } = ctx.params; - let gm = $.discord.gm(id, userId) + let gm = $.discord.gm(id, userId); if (gm == null && $.discord.isRoot(userId)) { - gm = $.discord.fakeGm({ id: userId }) + gm = $.discord.fakeGm({ id: userId }); } // check perms if (!$.discord.getPermissions(gm).canManageRoles) { - ctx.status = 403 - ctx.body = { err: 'cannot_manage_roles' } - return + ctx.status = 403; + ctx.body = { err: 'cannot_manage_roles' }; + return; } - const { message = null, categories = null } = ctx.request.body + const { message = null, categories = null } = ctx.request.body; // todo make less nasty await $.server.update(id, { - ...((message != null) ? { message } : {}), - ...((categories != null) ? { categories } : {}) - }) + ...(message != null ? { message } : {}), + ...(categories != null ? { categories } : {}), + }); - ctx.body = { ok: true } - }) + ctx.body = { ok: true }; + }); R.get('/api/admin/servers', async ctx => { - const { userId } = ctx.session + const { userId } = ctx.session; if (!$.discord.isRoot(userId)) { - return + return; } - ctx.body = $.discord.client.guilds.map(g => ({ url: `${process.env.APP_URL}/s/${g.id}`, name: g.name, members: g.members.array().length, roles: g.roles.array().length })) - }) + ctx.body = $.discord.client.guilds.map(g => ({ + url: `${process.env.APP_URL}/s/${g.id}`, + name: g.name, + members: g.members.array().length, + roles: g.roles.array().length, + })); + }); R.patch('/api/servers/:server/roles', async ctx => { - const { userId } = ctx.session - const { server } = ctx.params + const { userId } = ctx.session; + const { server } = ctx.params; - let gm = $.discord.gm(server, userId) + let gm = $.discord.gm(server, userId); if (gm == null && $.discord.isRoot(userId)) { - gm = $.discord.fakeGm({ id: userId }) + gm = $.discord.fakeGm({ id: userId }); } // check perms @@ -107,24 +112,24 @@ module.exports = (R, $) => { // return // } - const { added, removed } = ctx.request.body + const { added, removed } = ctx.request.body; - const allowedRoles = await $.server.getAllowedRoles(server) + const allowedRoles = await $.server.getAllowedRoles(server); - const pred = r => $.discord.safeRole(server, r) && allowedRoles.indexOf(r) !== -1 + const pred = r => $.discord.safeRole(server, r) && allowedRoles.indexOf(r) !== -1; if (added.length > 0) { - gm = await gm.addRoles(added.filter(pred)) + gm = await gm.addRoles(added.filter(pred)); } setTimeout(() => { if (removed.length > 0) { - gm.removeRoles(removed.filter(pred)) + gm.removeRoles(removed.filter(pred)); } - }, 1000) + }, 1000); // console.log('role patch', { added, removed, allowedRoles, addedFiltered: added.filterNot(pred), removedFiltered: removed.filterNot(pred) }) - ctx.body = { ok: true } - }) -} + ctx.body = { ok: true }; + }); +}; diff --git a/Server/api/servers_test.js b/Server/api/servers_test.js index 605c021..67b766b 100644 --- a/Server/api/servers_test.js +++ b/Server/api/servers_test.js @@ -1,26 +1,26 @@ module.exports = (R, $) => { R.get('/api/~/relevant-servers/:user', (ctx, next) => { // ctx.body = 'ok' - const srv = $.discord.getRelevantServers(ctx.params.user) - ctx.body = $.discord.presentableServers(srv, ctx.params.user) - return - }) + const srv = $.discord.getRelevantServers(ctx.params.user); + ctx.body = $.discord.presentableServers(srv, ctx.params.user); + return; + }); R.get('/api/~/roles/:id/:userId', (ctx, next) => { // ctx.body = 'ok' - const { id, userId } = ctx.params + const { id, userId } = ctx.params; - const srv = $.discord.client.guilds.get(id) + const srv = $.discord.client.guilds.get(id); if (srv === undefined) { - ctx.body = { err: 'not found' } - ctx.status = 404 - return + ctx.body = { err: 'not found' }; + ctx.status = 404; + return; } - const gm = srv.members.get(userId) - const roles = $.discord.presentableRoles(id, gm) + const gm = srv.members.get(userId); + const roles = $.discord.presentableRoles(id, gm); - ctx.boy = roles - }) -} + ctx.boy = roles; + }); +}; diff --git a/Server/index.js b/Server/index.js index 2268c7e..728974d 100644 --- a/Server/index.js +++ b/Server/index.js @@ -1,93 +1,96 @@ -require('dotenv').config({silent: true}) -const log = new (require('./logger'))('index') +require('dotenv').config({ silent: true }); +const log = new (require('./logger'))('index'); -const http = require('http') -const Koa = require('koa') -const app = new Koa() -const _io = require('socket.io') -const fs = require('fs') -const path = require('path') -const router = require('koa-better-router')().loadMethods() -const Roleypoly = require('./Roleypoly') -const ksuid = require('ksuid') +const http = require('http'); +const Koa = require('koa'); +const app = new Koa(); +const _io = require('socket.io'); +const fs = require('fs'); +const path = require('path'); +const router = require('koa-better-router')().loadMethods(); +const Roleypoly = require('./Roleypoly'); +const ksuid = require('ksuid'); // monkey patch async-reduce because F U T U R E -Array.prototype.areduce = async function (predicate, acc = []) { // eslint-disable-line +Array.prototype.areduce = async function(predicate, acc = []) { + // eslint-disable-line for (let i of this) { - acc = await predicate(acc, i) + acc = await predicate(acc, i); } - return acc -} + return acc; +}; -Array.prototype.filterNot = Array.prototype.filterNot || function (predicate) { - return this.filter(v => !predicate(v)) -} +Array.prototype.filterNot = + Array.prototype.filterNot || + function(predicate) { + return this.filter(v => !predicate(v)); + }; // Create the server and socket.io server -const server = http.createServer(app.callback()) -const io = _io(server, { transports: ['websocket'], path: '/api/socket.io' }) +const server = http.createServer(app.callback()); +const io = _io(server, { transports: ['websocket'], path: '/api/socket.io' }); -const M = new Roleypoly(router, io, app) // eslint-disable-line no-unused-vars +const M = new Roleypoly(router, io, app); // eslint-disable-line no-unused-vars -app.keys = [ process.env.APP_KEY ] +app.keys = [process.env.APP_KEY]; -const DEVEL = process.env.NODE_ENV === 'development' +const DEVEL = process.env.NODE_ENV === 'development'; -async function start () { - await M.awaitServices() +async function start() { + await M.awaitServices(); // body parser - const bodyParser = require('koa-bodyparser') - app.use(bodyParser({ types: ['json'] })) + const bodyParser = require('koa-bodyparser'); + app.use(bodyParser({ types: ['json'] })); // Compress - const compress = require('koa-compress') - app.use(compress()) + const compress = require('koa-compress'); + app.use(compress()); // SPA + Static if (process.env.NODE_ENV === 'production') { - const pub = path.resolve(path.join(__dirname, 'public')) - log.info('public path', pub) + const pub = path.resolve(path.join(__dirname, 'public')); + log.info('public path', pub); - const staticFiles = require('koa-static') + const staticFiles = require('koa-static'); // app.use(staticFiles(pub, { defer: true, gzip: true, br: true })) - const send = require('koa-send') + const send = require('koa-send'); app.use(async (ctx, next) => { if (ctx.path.startsWith('/api')) { - log.info('api breakout') - return next() + log.info('api breakout'); + return next(); } - const chkPath = path.resolve(path.join(pub, ctx.path)) - log.info('chkPath', chkPath) + const chkPath = path.resolve(path.join(pub, ctx.path)); + log.info('chkPath', chkPath); if (!chkPath.startsWith(pub)) { - return next() + return next(); } try { - fs.statSync(chkPath) - log.info('sync pass') - ctx.body = fs.readFileSync(chkPath) - ctx.type = path.extname(ctx.path) - log.info('body sent') - ctx.status = 200 - return next() + fs.statSync(chkPath); + log.info('sync pass'); + ctx.body = fs.readFileSync(chkPath); + ctx.type = path.extname(ctx.path); + log.info('body sent'); + ctx.status = 200; + return next(); } catch (e) { - log.warn('failed') + log.warn('failed'); if (ctx.path.startsWith('/static/')) { - return next() + return next(); } try { - ctx.body = fs.readFileSync(path.join(pub, 'index.html'), { encoding: 'utf-8' }) + ctx.body = fs.readFileSync(path.join(pub, 'index.html'), { encoding: 'utf-8' }); } catch (e) { - ctx.body = e.stack || e.trace - ctx.status = 500 + ctx.body = e.stack || e.trace; + ctx.status = 500; } - log.info('index sent') - ctx.status = 200 - return next() + log.info('index sent'); + ctx.status = 200; + return next(); } // try { @@ -95,8 +98,7 @@ async function start () { // } catch (e) { // send(ctx, 'index.html', { root: pub }) // } - - }) + }); // const sendOpts = {root: pub, index: 'index.html'} // // const sendOpts = {} // app.use(async (ctx, next) => { @@ -121,43 +123,52 @@ async function start () { // Request logger app.use(async (ctx, next) => { - let timeStart = new Date() + let timeStart = new Date(); try { - await next() + await next(); } catch (e) { - log.error(e) - ctx.status = ctx.status || 500 + log.error(e); + ctx.status = ctx.status || 500; if (DEVEL) { - ctx.body = ctx.body || e.stack + ctx.body = ctx.body || e.stack; } else { ctx.body = { - err: 'something terrible happened.' - } + err: 'something terrible happened.', + }; } } - let timeElapsed = new Date() - timeStart + let timeElapsed = new Date() - timeStart; - log.request(`${ctx.status} ${ctx.method} ${ctx.url} - ${ctx.ip} - took ${timeElapsed}ms`) + log.request( + `${ctx.status} ${ctx.method} ${ctx.url} - ${ctx.ip} - took ${timeElapsed}ms` + ); // return null - }) + }); - const session = require('koa-session') - app.use(session({ - key: 'roleypoly:sess', - maxAge: 'session', - siteOnly: true, - store: M.ctx.sessions, - genid: () => { return ksuid.randomSync().string } - }, app)) + const session = require('koa-session'); + app.use( + session( + { + key: 'roleypoly:sess', + maxAge: 'session', + siteOnly: true, + store: M.ctx.sessions, + genid: () => { + return ksuid.randomSync().string; + }, + }, + app + ) + ); - await M.mountRoutes() + await M.mountRoutes(); // SPA server - log.info(`starting HTTP server on ${process.env.APP_PORT || 6769}`) - server.listen(process.env.APP_PORT || 6769) + log.info(`starting HTTP server on ${process.env.APP_PORT || 6769}`); + server.listen(process.env.APP_PORT || 6769); } start().catch(e => { - log.fatal('app failed to start', e) -}) + log.fatal('app failed to start', e); +}); diff --git a/Server/logger.js b/Server/logger.js index 58513da..c01c86c 100644 --- a/Server/logger.js +++ b/Server/logger.js @@ -1,58 +1,59 @@ -const chalk = require('chalk') +const chalk = require('chalk'); // const { debug } = require('yargs').argv // process.env.DEBUG = process.env.DEBUG || debug // logger template// // const log = new (require('../logger'))('server/thing') class Logger { - constructor (name, debugOverride = false) { - this.name = name - this.debugOn = (process.env.DEBUG === 'true' || process.env.DEBUG === '*') || debugOverride + constructor(name, debugOverride = false) { + this.name = name; + this.debugOn = + process.env.DEBUG === 'true' || process.env.DEBUG === '*' || debugOverride; } - fatal (text, ...data) { - this.error(text, data) + fatal(text, ...data) { + this.error(text, data); if (typeof data[data.length - 1] === 'number') { - process.exit(data[data.length - 1]) + process.exit(data[data.length - 1]); } else { - process.exit(1) + process.exit(1); } - throw text + throw text; } - error (text, ...data) { - console.error(chalk.red.bold(`ERR ${this.name}:`) + `\n ${text}`, data) + error(text, ...data) { + console.error(chalk.red.bold(`ERR ${this.name}:`) + `\n ${text}`, data); } - warn (text, ...data) { - console.warn(chalk.yellow.bold(`WARN ${this.name}:`) + `\n ${text}`, data) + warn(text, ...data) { + console.warn(chalk.yellow.bold(`WARN ${this.name}:`) + `\n ${text}`, data); } - notice (text, ...data) { - console.log(chalk.cyan.bold(`NOTICE ${this.name}:`) + `\n ${text}`, data) + notice(text, ...data) { + console.log(chalk.cyan.bold(`NOTICE ${this.name}:`) + `\n ${text}`, data); } - info (text, ...data) { - console.info(chalk.blue.bold(`INFO ${this.name}:`) + `\n ${text}`, data) + info(text, ...data) { + console.info(chalk.blue.bold(`INFO ${this.name}:`) + `\n ${text}`, data); } - request (text, ...data) { - console.info(chalk.green.bold(`HTTP ${this.name}:`) + `\n ${text}`) + request(text, ...data) { + console.info(chalk.green.bold(`HTTP ${this.name}:`) + `\n ${text}`); } - debug (text, ...data) { + debug(text, ...data) { if (this.debugOn) { - console.log(chalk.gray.bold(`DEBUG ${this.name}:`) + `\n ${text}`, data) + console.log(chalk.gray.bold(`DEBUG ${this.name}:`) + `\n ${text}`, data); } } - sql (logger, ...data) { + sql(logger, ...data) { if (logger.debugOn) { - console.log(chalk.bold('DEBUG SQL:\n '), data) + console.log(chalk.bold('DEBUG SQL:\n '), data); } } } -module.exports = Logger +module.exports = Logger; diff --git a/Server/models/Server.js b/Server/models/Server.js index d93bd74..6269f17 100644 --- a/Server/models/Server.js +++ b/Server/models/Server.js @@ -1,14 +1,15 @@ module.exports = (sql, DataTypes) => { return sql.define('server', { - id: { // discord snowflake + id: { + // discord snowflake type: DataTypes.TEXT, - primaryKey: true + primaryKey: true, }, categories: { - type: DataTypes.JSON + type: DataTypes.JSON, }, message: { - type: DataTypes.TEXT - } - }) -} + type: DataTypes.TEXT, + }, + }); +}; diff --git a/Server/models/Session.js b/Server/models/Session.js index b7f6c8a..38fe049 100644 --- a/Server/models/Session.js +++ b/Server/models/Session.js @@ -2,6 +2,6 @@ module.exports = (sequelize, DataTypes) => { return sequelize.define('session', { id: { type: DataTypes.TEXT, primaryKey: true }, maxAge: DataTypes.BIGINT, - data: DataTypes.JSONB - }) -} + data: DataTypes.JSONB, + }); +}; diff --git a/Server/models/index.js b/Server/models/index.js index cdd176e..253d328 100644 --- a/Server/models/index.js +++ b/Server/models/index.js @@ -1,37 +1,37 @@ -const log = new (require('../logger'))('models/index') -const glob = require('glob') -const path = require('path') -const util = require('../util/model-methods') +const log = new (require('../logger'))('models/index'); +const glob = require('glob'); +const path = require('path'); +const util = require('../util/model-methods'); -module.exports = (sql) => { - const models = {} - const modelFiles = glob.sync('./models/**/!(index).js') - log.debug('found models', modelFiles) +module.exports = sql => { + const models = {}; + const modelFiles = glob.sync('./models/**/!(index).js'); + log.debug('found models', modelFiles); - modelFiles.forEach((v) => { - let name = path.basename(v).replace('.js', '') + modelFiles.forEach(v => { + let name = path.basename(v).replace('.js', ''); if (v === './models/index.js') { - log.debug('index.js hit, skipped') - return + log.debug('index.js hit, skipped'); + return; } try { - log.debug('importing..', v.replace('models/', '')) - let model = sql.import(v.replace('models/', '')) - models[name] = model + log.debug('importing..', v.replace('models/', '')); + let model = sql.import(v.replace('models/', '')); + models[name] = model; } catch (err) { - log.fatal('error importing model ' + v, err) - process.exit(-1) + log.fatal('error importing model ' + v, err); + process.exit(-1); } - }) + }); - Object.keys(models).forEach((v) => { + Object.keys(models).forEach(v => { if (models[v].hasOwnProperty('__associations')) { - models[v].__associations(models) + models[v].__associations(models); } if (models[v].hasOwnProperty('__instanceMethods')) { - models[v].__instanceMethods(models[v]) + models[v].__instanceMethods(models[v]); } - }) + }); - return models -} + return models; +}; diff --git a/Server/package.json b/Server/package.json index 5a7d8c4..fcde5aa 100644 --- a/Server/package.json +++ b/Server/package.json @@ -6,10 +6,12 @@ "start": "standard && node index.js", "fix": "standard --fix", "dev": "pm2 start index.js --watch", + "lint:prettier": "prettier -c '**/*.{ts,tsx,css,yml,yaml,md,json,js,jsx}'", "pm2": "pm2" }, "dependencies": { "@discordjs/uws": "^11.149.1", + "@roleypoly/rpc": "^3.0.0-alpha.12", "chalk": "^2.4.2", "discord.js": "^11.4.2", "dotenv": "^7.0.0", @@ -34,5 +36,8 @@ "socket.io": "^2.2.0", "superagent": "^5.0.2", "uuid": "^3.3.2" + }, + "devDependencies": { + "prettier": "^1.19.1" } } diff --git a/Server/services/Service.js b/Server/services/Service.js index e020af4..4ea5aa6 100644 --- a/Server/services/Service.js +++ b/Server/services/Service.js @@ -1,10 +1,10 @@ -const Logger = require('../logger') +const Logger = require('../logger'); class Service { - constructor (ctx) { - this.ctx = ctx - this.log = new Logger(this.constructor.name) + constructor(ctx) { + this.ctx = ctx; + this.log = new Logger(this.constructor.name); } } -module.exports = Service +module.exports = Service; diff --git a/Server/services/discord-rpc.js b/Server/services/discord-rpc.js new file mode 100644 index 0000000..61bce10 --- /dev/null +++ b/Server/services/discord-rpc.js @@ -0,0 +1,4 @@ +const Service = require('./Service'); +const DiscordRPC = require('@roleypoly/rpc/discord'); + +class DiscordRPCService extends Service {} diff --git a/Server/services/discord.js b/Server/services/discord.js index 214f3e6..c3b9d72 100644 --- a/Server/services/discord.js +++ b/Server/services/discord.js @@ -1,124 +1,130 @@ -const Service = require('./Service') -const discord = require('discord.js') -const superagent = require('superagent') +const Service = require('./Service'); +const discord = require('discord.js'); +const superagent = require('superagent'); class DiscordService extends Service { - constructor (ctx) { - super(ctx) + constructor(ctx) { + super(ctx); - this.botToken = process.env.DISCORD_BOT_TOKEN - this.clientId = process.env.DISCORD_CLIENT_ID - this.clientSecret = process.env.DISCORD_CLIENT_SECRET - this.oauthCallback = process.env.OAUTH_AUTH_CALLBACK - this.botCallback = `${ctx.config.appUrl}/api/oauth/bot/callback` - this.appUrl = process.env.APP_URL - this.isBot = process.env.IS_BOT === 'true' || false - this.rootUsers = new Set((process.env.ROOT_USERS||'').split(',')) + this.botToken = process.env.DISCORD_BOT_TOKEN; + this.clientId = process.env.DISCORD_CLIENT_ID; + this.clientSecret = process.env.DISCORD_CLIENT_SECRET; + this.oauthCallback = process.env.OAUTH_AUTH_CALLBACK; + this.botCallback = `${ctx.config.appUrl}/api/oauth/bot/callback`; + this.appUrl = process.env.APP_URL; + this.isBot = process.env.IS_BOT === 'true' || false; + this.rootUsers = new Set((process.env.ROOT_USERS || '').split(',')); - this.client = new discord.Client() - this.client.options.disableEveryone = true + this.client = new discord.Client(); + this.client.options.disableEveryone = true; - this.cmds = this._cmds() + this.cmds = this._cmds(); - this.startBot() + this.startBot(); } - ownGm (server) { - return this.gm(server, this.client.user.id) + ownGm(server) { + return this.gm(server, this.client.user.id); } - fakeGm({id = 0, nickname = '[none]', displayHexColor = '#ffffff'}) { - return { id, nickname, displayHexColor, __faked: true, roles: { has() {return false} } } + fakeGm({ id = 0, nickname = '[none]', displayHexColor = '#ffffff' }) { + return { + id, + nickname, + displayHexColor, + __faked: true, + roles: { + has() { + return false; + }, + }, + }; } isRoot(id) { - return this.rootUsers.has(id) + return this.rootUsers.has(id); } - async startBot () { - await this.client.login(this.botToken) + async startBot() { + await this.client.login(this.botToken); // not all roleypolys are bots. if (this.isBot) { - this.log.info('this roleypoly is a bot') - this.client.on('message', this.handleMessage.bind(this)) - this.client.on('guildCreate', this.handleJoin.bind(this)) + this.log.info('this roleypoly is a bot'); + this.client.on('message', this.handleMessage.bind(this)); + this.client.on('guildCreate', this.handleJoin.bind(this)); } for (let server of this.client.guilds.array()) { - await this.ctx.server.ensure(server) + await this.ctx.server.ensure(server); } } - getRelevantServers (userId) { - return this.client.guilds.filter((g) => g.members.has(userId)) + getRelevantServers(userId) { + return this.client.guilds.filter(g => g.members.has(userId)); } - gm (serverId, userId) { - return this.client.guilds.get(serverId).members.get(userId) + gm(serverId, userId) { + return this.client.guilds.get(serverId).members.get(userId); } - getRoles (server) { - return this.client.guilds.get(server).roles + getRoles(server) { + return this.client.guilds.get(server).roles; } - getPermissions (gm) { + getPermissions(gm) { if (this.isRoot(gm.id)) { return { isAdmin: true, - canManageRoles: true - } + canManageRoles: true, + }; } return { isAdmin: gm.permissions.hasPermission('ADMINISTRATOR'), - canManageRoles: gm.permissions.hasPermission('MANAGE_ROLES', false, true) - } + canManageRoles: gm.permissions.hasPermission('MANAGE_ROLES', false, true), + }; } - safeRole (server, role) { - const r = this.getRoles(server).get(role) - return r.editable && !r.hasPermission('MANAGE_ROLES', false, true) + safeRole(server, role) { + const r = this.getRoles(server).get(role); + return r.editable && !r.hasPermission('MANAGE_ROLES', false, true); } // oauth step 2 flow, grab the auth token via code - async getAuthToken (code) { - const url = 'https://discordapp.com/api/oauth2/token' + async getAuthToken(code) { + const url = 'https://discordapp.com/api/oauth2/token'; try { - const rsp = - await superagent - .post(url) - .set('Content-Type', 'application/x-www-form-urlencoded') - .send({ - client_id: this.clientId, - client_secret: this.clientSecret, - grant_type: 'authorization_code', - code: code, - redirect_uri: this.oauthCallback - }) + const rsp = await superagent + .post(url) + .set('Content-Type', 'application/x-www-form-urlencoded') + .send({ + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: 'authorization_code', + code: code, + redirect_uri: this.oauthCallback, + }); - return rsp.body + return rsp.body; } catch (e) { - this.log.error('getAuthToken failed', e) - throw e + this.log.error('getAuthToken failed', e); + throw e; } } - async getUser (authToken) { - const url = 'https://discordapp.com/api/v6/users/@me' + async getUser(authToken) { + const url = 'https://discordapp.com/api/v6/users/@me'; try { if (authToken == null || authToken === '') { - throw new Error('not logged in') + throw new Error('not logged in'); } - const rsp = - await superagent - .get(url) - .set('Authorization', `Bearer ${authToken}`) - return rsp.body + const rsp = await superagent.get(url).set('Authorization', `Bearer ${authToken}`); + return rsp.body; } catch (e) { - this.log.error('getUser error', e) - throw e + this.log.error('getUser error', e); + throw e; } } @@ -146,96 +152,108 @@ class DiscordService extends Service { // returns oauth authorize url with IDENTIFY permission // we only need IDENTIFY because we only use it for matching IDs from the bot - getAuthUrl (state) { - return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&redirect_uri=${this.oauthCallback}&response_type=code&scope=identify&state=${state}` + getAuthUrl(state) { + return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&redirect_uri=${this.oauthCallback}&response_type=code&scope=identify&state=${state}`; } // returns the bot join url with MANAGE_ROLES permission // MANAGE_ROLES is the only permission we really need. - getBotJoinUrl () { - return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&scope=bot&permissions=268435456` + getBotJoinUrl() { + return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&scope=bot&permissions=268435456`; } - mentionResponse (message) { - message.channel.send(`🔰 Assign your roles here! <${this.appUrl}/s/${message.guild.id}>`, { disableEveryone: true }) + mentionResponse(message) { + message.channel.send( + `🔰 Assign your roles here! <${this.appUrl}/s/${message.guild.id}>`, + { disableEveryone: true } + ); } - _cmds () { + _cmds() { const cmds = [ { regex: /say (.*)/, - handler (message, matches, r) { - r(matches[0]) - } + handler(message, matches, r) { + r(matches[0]); + }, }, { regex: /set username (.*)/, - async handler (message, matches) { - const { username } = this.client.user - await this.client.user.setUsername(matches[0]) - message.channel.send(`Username changed from ${username} to ${matches[0]}`) - } + async handler(message, matches) { + const { username } = this.client.user; + await this.client.user.setUsername(matches[0]); + message.channel.send(`Username changed from ${username} to ${matches[0]}`); + }, }, { regex: /stats/, - async handler (message, matches) { + async handler(message, matches) { const t = [ `**Stats** 📈`, '', - `👩‍❤️‍👩 **Users Served:** ${this.client.guilds.reduce((acc, g) => acc + g.memberCount, 0)}`, + `👩‍❤️‍👩 **Users Served:** ${this.client.guilds.reduce( + (acc, g) => acc + g.memberCount, + 0 + )}`, `🔰 **Servers:** ${this.client.guilds.size}`, - `💮 **Roles Seen:** ${this.client.guilds.reduce((acc, g) => acc + g.roles.size, 0)}` - ] - message.channel.send(t.join('\n')) - } - } + `💮 **Roles Seen:** ${this.client.guilds.reduce( + (acc, g) => acc + g.roles.size, + 0 + )}`, + ]; + message.channel.send(t.join('\n')); + }, + }, ] // prefix regex with ^ for ease of code - .map(({regex, ...rest}) => ({ regex: new RegExp(`^${regex.source}`, regex.flags), ...rest })) + .map(({ regex, ...rest }) => ({ + regex: new RegExp(`^${regex.source}`, regex.flags), + ...rest, + })); - return cmds + return cmds; } - async handleCommand (message) { - const cmd = message.content.replace(`<@${this.client.user.id}> `, '') - this.log.debug(`got command from ${message.author.username}`, cmd) + async handleCommand(message) { + const cmd = message.content.replace(`<@${this.client.user.id}> `, ''); + this.log.debug(`got command from ${message.author.username}`, cmd); for (let { regex, handler } of this.cmds) { - const match = regex.exec(cmd) + const match = regex.exec(cmd); if (match !== null) { - this.log.debug('command accepted', { cmd, match }) + this.log.debug('command accepted', { cmd, match }); try { - await handler.call(this, message, match.slice(1)) - return + await handler.call(this, message, match.slice(1)); + return; } catch (e) { - this.log.error('command errored', { e, cmd, message }) - message.channel.send(`❌ **An error occured.** ${e}`) - return + this.log.error('command errored', { e, cmd, message }); + message.channel.send(`❌ **An error occured.** ${e}`); + return; } } } // nothing matched? - this.mentionResponse(message) + this.mentionResponse(message); } - handleMessage (message) { - if (message.author.bot && message.channel.type !== 'text') { // drop bot messages and dms - return + handleMessage(message) { + if (message.author.bot && message.channel.type !== 'text') { + // drop bot messages and dms + return; } if (message.mentions.users.has(this.client.user.id)) { if (this.rootUsers.has(message.author.id)) { - this.handleCommand(message) + this.handleCommand(message); } else { - this.mentionResponse(message) + this.mentionResponse(message); } } } - async handleJoin (guild) { - await this.ctx.server.ensure(guild) + async handleJoin(guild) { + await this.ctx.server.ensure(guild); } - } -module.exports = DiscordService +module.exports = DiscordService; diff --git a/Server/services/presentation.js b/Server/services/presentation.js index 0a45d6c..b3114ac 100644 --- a/Server/services/presentation.js +++ b/Server/services/presentation.js @@ -1,62 +1,67 @@ -const Service = require('./Service') -const LRU = require('lru-cache') +const Service = require('./Service'); +const LRU = require('lru-cache'); class PresentationService extends Service { - constructor (ctx) { - super(ctx) - this.M = ctx.M - this.discord = ctx.discord + constructor(ctx) { + super(ctx); + this.M = ctx.M; + this.discord = ctx.discord; - this.cache = new LRU({ max: 500, maxAge: 100 * 60 * 5 }) + this.cache = new LRU({ max: 500, maxAge: 100 * 60 * 5 }); } - serverSlug (server) { + serverSlug(server) { return { id: server.id, name: server.name, ownerID: server.ownerID, - icon: server.icon - } + icon: server.icon, + }; } - async oldPresentableServers (collection, userId) { - let servers = [] + async oldPresentableServers(collection, userId) { + let servers = []; for (let server of collection.array()) { - const gm = server.members.get(userId) + const gm = server.members.get(userId); - servers.push(await this.presentableServer(server, gm)) + servers.push(await this.presentableServer(server, gm)); } - return servers + return servers; } - async presentableServers (collection, userId) { + async presentableServers(collection, userId) { return collection.array().areduce(async (acc, server) => { - const gm = server.members.get(userId) - acc.push(await this.presentableServer(server, gm, { incRoles: false })) - return acc - }) + const gm = server.members.get(userId); + acc.push(await this.presentableServer(server, gm, { incRoles: false })); + return acc; + }); } - async presentableServer (server, gm, { incRoles = true } = {}) { - const sd = await this.ctx.server.get(server.id) + async presentableServer(server, gm, { incRoles = true } = {}) { + const sd = await this.ctx.server.get(server.id); return { id: server.id, gm: { nickname: gm.nickname, - color: gm.displayHexColor + color: gm.displayHexColor, }, server: this.serverSlug(server), - roles: (incRoles) ? (await this.rolesByServer(server, sd)).map(r => ({ ...r, selected: gm.roles.has(r.id) })) : [], + roles: incRoles + ? (await this.rolesByServer(server, sd)).map(r => ({ + ...r, + selected: gm.roles.has(r.id), + })) + : [], message: sd.message, categories: sd.categories, - perms: this.discord.getPermissions(gm) - } + perms: this.discord.getPermissions(gm), + }; } - async rolesByServer (server) { + async rolesByServer(server) { return server.roles .filter(r => r.id !== server.id) // get rid of @everyone .map(r => ({ @@ -64,9 +69,9 @@ class PresentationService extends Service { color: r.color, name: r.name, position: r.position, - safe: this.discord.safeRole(server.id, r.id) - })) + safe: this.discord.safeRole(server.id, r.id), + })); } } -module.exports = PresentationService +module.exports = PresentationService; diff --git a/Server/services/server.js b/Server/services/server.js index fef8284..45709b6 100644 --- a/Server/services/server.js +++ b/Server/services/server.js @@ -1,66 +1,64 @@ -const Service = require('./Service') +const Service = require('./Service'); class ServerService extends Service { - constructor (ctx) { - super(ctx) - this.Server = ctx.M.Server - this.P = ctx.P + constructor(ctx) { + super(ctx); + this.Server = ctx.M.Server; + this.P = ctx.P; } - async ensure (server) { - let srv + async ensure(server) { + let srv; try { - srv = await this.get(server.id) - } catch (e) { - - } + srv = await this.get(server.id); + } catch (e) {} if (srv == null) { return this.create({ id: server.id, message: '', - categories: {} - }) + categories: {}, + }); } } - create ({ id, message, categories }) { - const srv = this.Server.build({ id, message, categories }) + create({ id, message, categories }) { + const srv = this.Server.build({ id, message, categories }); - return srv.save() + return srv.save(); } - async update (id, newData) { - const srv = await this.get(id, false) + async update(id, newData) { + const srv = await this.get(id, false); - return srv.update(newData) + return srv.update(newData); } - async get (id, plain = true) { + async get(id, plain = true) { const s = await this.Server.findOne({ where: { - id - } - }) + id, + }, + }); if (!plain) { - return s + return s; } - return s.get({ plain: true }) + return s.get({ plain: true }); } - async getAllowedRoles (id) { - const server = await this.get(id) + async getAllowedRoles(id) { + const server = await this.get(id); return Object.values(server.categories).reduce((acc, c) => { if (c.hidden !== true) { - return acc.concat(c.roles) + return acc.concat(c.roles); } - return acc - }, []) + return acc; + }, []); } } -module.exports = ServerService +module.exports = ServerService; diff --git a/Server/services/sessions.js b/Server/services/sessions.js index 5f924f1..05d5894 100644 --- a/Server/services/sessions.js +++ b/Server/services/sessions.js @@ -1,40 +1,40 @@ -const Service = require('./Service') +const Service = require('./Service'); class SessionsService extends Service { - constructor (ctx) { - super(ctx) - this.Session = ctx.M.Session + constructor(ctx) { + super(ctx); + this.Session = ctx.M.Session; } - async get (id, {rolling}) { - const user = await this.Session.findOne({ where: { id } }) + async get(id, { rolling }) { + const user = await this.Session.findOne({ where: { id } }); if (user === null) { - return null + return null; } - return user.data + return user.data; } - async set (id, data, {maxAge, rolling, changed}) { - let session = await this.Session.findOne({ where: { id } }) + async set(id, data, { maxAge, rolling, changed }) { + let session = await this.Session.findOne({ where: { id } }); if (session === null) { - session = this.Session.build({ id }) + session = this.Session.build({ id }); } - session.data = data - session.maxAge = maxAge + session.data = data; + session.maxAge = maxAge; - return session.save() + return session.save(); } - async destroy (id) { - const sess = await this.Session.findOne({ where: { id } }) + async destroy(id) { + const sess = await this.Session.findOne({ where: { id } }); if (sess != null) { - return sess.destroy() + return sess.destroy(); } } } -module.exports = SessionsService +module.exports = SessionsService; diff --git a/Server/util/model-methods.js b/Server/util/model-methods.js index 6ce720e..18abb18 100644 --- a/Server/util/model-methods.js +++ b/Server/util/model-methods.js @@ -1,10 +1,10 @@ -const ksuid = require('ksuid') +const ksuid = require('ksuid'); module.exports = { - ksuid (field = 'id') { - return async function () { - this.id = await ksuid.random() - return this - } - } -} + ksuid(field = 'id') { + return async function() { + this.id = await ksuid.random(); + return this; + }; + }, +}; diff --git a/Server/yarn.lock b/Server/yarn.lock index 42e8fd0..4043689 100644 --- a/Server/yarn.lock +++ b/Server/yarn.lock @@ -23,6 +23,13 @@ resolved "https://registry.yarnpkg.com/@discordjs/uws/-/uws-11.149.1.tgz#2e86f2825f43bed43d2e69eaf30a07d2dd8e8946" integrity sha512-TmbwZaeXDSCq0ckmf2q10Fkt1220gu9AZJ/UvtQjsi2jyJDjy0i0OwL4/eb3vc9Cwr0mpC9EbfzltQ2si0qUiQ== +"@improbable-eng/grpc-web@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web/-/grpc-web-0.11.0.tgz#c9d4097b4a947384e3dc0234299d910668a6e7ad" + integrity sha512-SS2YP6iHyZ7TSSuCShnSo9xJyUkNZHEhEPJpTSUcNoULe1LuLEk52OKHY+VW9XB0qXstejpHgZq2Hx+69PThiw== + dependencies: + browser-headers "^0.4.0" + "@opencensus/core@^0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.8.tgz#df01f200c2d2fbfe14dae129a1a86fb87286db92" @@ -108,6 +115,14 @@ eventemitter2 "^4.1.0" ws "^3.0.0" +"@roleypoly/rpc@^3.0.0-alpha.12": + version "3.0.0-alpha.12" + resolved "https://registry.yarnpkg.com/@roleypoly/rpc/-/rpc-3.0.0-alpha.12.tgz#61e519755ae5103a191f253e63302bed252e9ea8" + integrity sha512-Tl7G/yGF/3FmoF3GoYFZ2JdpSH7Fv5NGGbDB7KbVum+GOmnIoG7D8ETUb5Ge2YHpTYLoJSAmVhDGiuI8o3QC6A== + dependencies: + "@improbable-eng/grpc-web" "0.11.0" + google-protobuf "3.10.0" + "@types/node@*": version "10.12.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.1.tgz#da61b64a2930a80fa708e57c45cd5441eb379d5b" @@ -434,6 +449,11 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +browser-headers@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/browser-headers/-/browser-headers-0.4.1.tgz#4308a7ad3b240f4203dbb45acedb38dc2d65dd02" + integrity sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg== + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -1427,6 +1447,11 @@ globals@^11.7.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.8.0.tgz#c1ef45ee9bed6badf0663c5cb90e8d1adec1321d" integrity sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA== +google-protobuf@3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.10.0.tgz#f36171128090615049f9b0e42252b1a10a4bc534" + integrity sha512-d0cMO8TJ6xtB/WrVHCv5U81L2ulX+aCD58IljyAN6mHwdHHJ2jbcauX5glvivi3s3hx7EYEo7eUA9WftzamMnw== + graceful-fs@^4.1.11: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2785,6 +2810,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + prism-media@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-0.0.3.tgz#8842d4fae804f099d3b48a9a38e3c2bab6f4855b"