mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-04-25 12:19:10 +00:00
lerna: complete refactor!
This commit is contained in:
parent
51dd8bd6b1
commit
e1bd5747b3
12 changed files with 83 additions and 71 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -10,3 +10,5 @@ node_modules
|
|||
|
||||
yarn-error\.log
|
||||
*.log
|
||||
|
||||
\.next/
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -20,7 +20,7 @@ export type ServerModel = {
|
|||
export type PresentableServer = ServerModel & {
|
||||
id: string,
|
||||
gm?: {
|
||||
color: number | string,
|
||||
color?: number | string,
|
||||
nickname: string,
|
||||
roles: string[]
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const next = require('next')
|
||||
|
||||
const connector = ({ dev }) => {
|
||||
return next({ dev })
|
||||
return next({ dev, dir: __dirname })
|
||||
}
|
||||
|
||||
module.exports = connector
|
||||
|
|
Loading…
Add table
Reference in a new issue