lerna: complete refactor!

This commit is contained in:
41666 2019-04-03 07:12:01 -05:00
parent 51dd8bd6b1
commit e1bd5747b3
No known key found for this signature in database
GPG key ID: BC51D07640DC10AF
12 changed files with 83 additions and 71 deletions

2
.gitignore vendored
View file

@ -10,3 +10,5 @@ node_modules
yarn-error\.log
*.log
\.next/

View file

@ -105,6 +105,8 @@ class Roleypoly {
throw log.fatal('DB_URL not set.')
}
await this.ctx.ui.prepare()
const sequelize = new Sequelize(dbUrl, { logging: log.sql.bind(log, log) })
this.ctx.sql = sequelize
this.M = fetchModels(sequelize)
@ -132,8 +134,6 @@ class Roleypoly {
}
async loadRoutes (forceClear: boolean = false) {
await this.ctx.ui.prepare()
this.router = betterRouter().loadMethods()
fetchApis(this.router, this.ctx, { forceClear })
// this.ctx.RPC.hookRoutes(this.router)
@ -156,15 +156,16 @@ class Roleypoly {
// hot-reloading system
log.info('API hot-reloading is active.')
const chokidar = require('chokidar')
const path = require('path')
let hotMiddleware = mw
this.__apiWatcher = chokidar.watch('api/**')
this.__apiWatcher = chokidar.watch(path.join(__dirname, 'api/**'))
this.__apiWatcher.on('all', async (path) => {
log.info('reloading APIs...', path)
hotMiddleware = await this.loadRoutes(true)
})
this.__rpcWatcher = chokidar.watch('rpc/**')
this.__rpcWatcher = chokidar.watch(path.join(__dirname, 'rpc/**'))
this.__rpcWatcher.on('all', (path) => {
log.info('reloading RPCs...', path)
this.ctx.RPC.reload()

View file

@ -1,15 +1,14 @@
// @flow
import logger from '../logger'
import glob from 'glob'
import type { Router, AppContext } from '../Roleypoly'
import path from 'path'
import logger from '../logger'
const log = logger(__filename)
const PROD = process.env.NODE_ENV === 'production'
export default async (router: Router, ctx: AppContext, { forceClear = false }: { forceClear: boolean } = {}) => {
const apis = glob.sync(`./api/**/!(index).js`)
const apis = glob.sync(`${__dirname}/**/!(index).js`).map(v => v.replace(__dirname, '.'))
log.debug('found apis', apis)
for (let a of apis) {
@ -19,12 +18,11 @@ export default async (router: Router, ctx: AppContext, { forceClear = false }: {
}
log.debug(`mounting ${a}`)
try {
const pathname = a.replace('api/', '')
if (forceClear) {
delete require.cache[require.resolve(pathname)]
delete require.cache[require.resolve(a)]
}
// $FlowFixMe this isn't an important error. potentially dangerous, but irrelevant.
require(pathname).default(router, ctx)
require(a).default(router, ctx)
} catch (e) {
log.error(`couldn't mount ${a}`, e)
}

View file

@ -15,18 +15,18 @@ export type Models = {
export default (sql: Sequelize): Models => {
const models: Models = {}
const modelFiles = glob.sync('./models/**/!(index).js')
const modelFiles = glob.sync(`${__dirname}/**/!(index).js`).map(v => v.replace(__dirname, '.'))
log.debug('found models', modelFiles)
modelFiles.forEach((v) => {
let name = path.basename(v).replace('.js', '')
if (v === './models/index.js') {
if (v === `./index.js`) {
log.debug('index.js hit, skipped')
return
}
try {
log.debug('importing..', v.replace('models/', ''))
let model = sql.import(v.replace('models/', ''))
log.debug('importing..', v)
let model = sql.import(v)
models[name] = model
} catch (err) {
log.fatal('error importing model ' + v, err)

View file

@ -11,14 +11,14 @@ export default (ctx: AppContext, forceClear: ?boolean = false): {
[rpc: string]: Function
} => {
let map = {}
const apis = glob.sync(`./rpc/**/!(index).js`)
const apis = glob.sync(`${__dirname}/**/!(index).js`).map(v => v.replace(__dirname, '.'))
log.debug('found rpcs', apis)
for (let a of apis) {
const filename = path.basename(a)
const dirname = path.dirname(a)
const pathname = a.replace('rpc/', '')
const pathname = a
delete require.cache[require.resolve(pathname)]
// internal stuff

View file

@ -95,12 +95,12 @@ export const member = (
$: AppContext,
fn: (ctx: Context, server: string, ...args: any[]) => any,
silent: boolean = false
) => authed($, (
) => authed($, async (
ctx: Context,
server: string,
...args: any[]
) => {
if ($.discord.isMember(server, ctx.session.userId)) {
if (await $.discord.isMember(server, ctx.session.userId)) {
return fn(ctx, server, ...args)
}

View file

@ -3,6 +3,7 @@ import { type AppContext } from '../Roleypoly'
import { type Context } from 'koa'
import { type Guild } from 'eris'
import * as secureAs from './_security'
import RPCError from './_error'
export default ($: AppContext) => ({
@ -24,23 +25,18 @@ export default ($: AppContext) => ({
return $.P.serverSlug(srv)
},
getServer: secureAs.member($, (ctx: Context, id: string) => {
getServer: secureAs.member($, async (ctx: Context, id: string) => {
const { userId } = (ctx.session: { userId: string })
const srv = $.discord.client.guilds.get(id)
const srv = await $.discord.fetcher.getGuild(id)
if (srv == null) {
return { err: 'not_found' }
throw new RPCError('server not found', 404)
}
let gm
if (srv.members.has(userId)) {
gm = $.discord.gm(id, userId)
} else if ($.discord.isRoot(userId)) {
gm = $.discord.fakeGm({ id: userId })
}
let gm = await $.discord.gm(id, userId, { canFake: true })
if (gm == null) {
return { err: 'not_found' }
throw new RPCError('server not found', 404)
}
return $.P.presentableServer(srv, gm)

View file

@ -2,33 +2,71 @@
import type { IFetcher } from './types'
import type DiscordSvc from '../discord'
import type ErisClient, { User, Member, Guild } from 'eris'
import LRU from 'lru-cache'
import logger from '../../logger'
const log = logger(__filename)
export default class BotFetcher implements IFetcher {
ctx: DiscordSvc
client: ErisClient
cache: LRU<string, Guild | Member | User>
constructor (ctx: DiscordSvc) {
this.ctx = ctx
this.client = ctx.client
this.cache = new LRU({
max: 50,
maxAge: 1000 * 60 * 10
})
}
getUser = async (id: string): Promise<?User> => {
if (this.cache.has(`U:${id}`)) {
log.debug('user cache hit')
return this.cache.get(`U:${id}`)
}
log.debug('user cache miss')
try {
return await this.client.getRESTUser(id)
const u = await this.client.getRESTUser(id)
this.cache.set(`U:${id}`, u)
return u
} catch (e) {
return null
}
}
getMember = async (server: string, user: string): Promise<?Member> => {
if (this.cache.has(`M:${server}:${user}`)) {
log.debug('member cache hit')
return this.cache.get(`M:${server}:${user}`)
}
log.debug('member cache miss')
try {
return await this.client.getRESTGuildMember(server, user)
const m = await this.client.getRESTGuildMember(server, user)
this.cache.set(`M:${server}:${user}`, m)
// $FlowFixMe
m.guild = await this.getGuild(server) // we have to prefill this for whatever reason
return m
} catch (e) {
return null
}
}
getGuild = async (server: string): Promise<?Guild> => {
if (this.cache.has(`G:${server}`)) {
log.debug('guild cache hit')
return this.cache.get(`G:${server}`)
}
log.debug('guild cache miss')
try {
return await this.client.getRESTGuild(server)
const g = await this.client.getRESTGuild(server)
this.cache.set(`G:${server}`, g)
return g
} catch (e) {
return null
}

View file

@ -4,41 +4,18 @@ import LRU from 'lru-cache'
import { type AppContext } from '../Roleypoly'
import { type Models } from '../models'
import { type ServerModel } from '../models/Server'
import type DiscordService, { Permissions } from './discord'
import type DiscordService from './discord'
import {
type Guild,
type Member,
type Collection
} from 'eris'
import type {
Member,
PresentableServer,
ServerSlug,
PresentableRole
} from '@roleypoly/types'
import areduce from '../util/areduce'
export type ServerSlug = {
id: string,
name: string,
ownerID: string,
icon: string
}
export type PresentableRole = {
id: string,
color: number,
name: string,
position: number,
safe: boolean
}
export type PresentableServer = ServerModel & {
id: string,
gm?: {
color: number | string,
nickname: string,
roles: string[]
},
server: ServerSlug,
roles: ?PresentableRole[],
perms: Permissions
}
class PresentationService extends Service {
cache: LRU
M: Models
@ -61,8 +38,8 @@ class PresentationService extends Service {
}
}
presentableServers (collection: Collection<string, Guild>, userId: string) {
return areduce(collection.array(), async (acc, server) => {
presentableServers (collection: Collection<Guild>, userId: string) {
return areduce(Array.from(collection.values()), async (acc, server) => {
const gm = server.members.get(userId)
if (gm == null) {
throw new Error(`somehow this guildmember ${userId} of ${server.id} didn't exist.`)
@ -79,12 +56,12 @@ class PresentationService extends Service {
return {
id: server.id,
gm: {
nickname: gm.nickname || gm.user.username,
color: gm.displayHexColor,
roles: gm.roles.keyArray()
nickname: gm.nick || gm.user.username,
color: gm?.color,
roles: gm.roles
},
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.includes(r.id) })) : [],
message: sd.message,
categories: sd.categories,
perms: this.discord.getPermissions(gm)

View file

@ -5,7 +5,7 @@ import { addAwaitOutsideToReplServer } from 'await-outside'
import Roleypoly from '../Roleypoly'
import chokidar from 'chokidar'
import logger from '../logger'
process.env.DEBUG = false
// process.env.DEBUG = false
process.env.IS_BOT = false
dotenv.config()

View file

@ -20,7 +20,7 @@ export type ServerModel = {
export type PresentableServer = ServerModel & {
id: string,
gm?: {
color: number | string,
color?: number | string,
nickname: string,
roles: string[]
},

View file

@ -1,7 +1,7 @@
const next = require('next')
const connector = ({ dev }) => {
return next({ dev })
return next({ dev, dir: __dirname })
}
module.exports = connector