mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-04-25 20:19:12 +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
|
yarn-error\.log
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
\.next/
|
||||||
|
|
|
@ -105,6 +105,8 @@ class Roleypoly {
|
||||||
throw log.fatal('DB_URL not set.')
|
throw log.fatal('DB_URL not set.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.ctx.ui.prepare()
|
||||||
|
|
||||||
const sequelize = new Sequelize(dbUrl, { logging: log.sql.bind(log, log) })
|
const sequelize = new Sequelize(dbUrl, { logging: log.sql.bind(log, log) })
|
||||||
this.ctx.sql = sequelize
|
this.ctx.sql = sequelize
|
||||||
this.M = fetchModels(sequelize)
|
this.M = fetchModels(sequelize)
|
||||||
|
@ -132,8 +134,6 @@ class Roleypoly {
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadRoutes (forceClear: boolean = false) {
|
async loadRoutes (forceClear: boolean = false) {
|
||||||
await this.ctx.ui.prepare()
|
|
||||||
|
|
||||||
this.router = betterRouter().loadMethods()
|
this.router = betterRouter().loadMethods()
|
||||||
fetchApis(this.router, this.ctx, { forceClear })
|
fetchApis(this.router, this.ctx, { forceClear })
|
||||||
// this.ctx.RPC.hookRoutes(this.router)
|
// this.ctx.RPC.hookRoutes(this.router)
|
||||||
|
@ -156,15 +156,16 @@ class Roleypoly {
|
||||||
// hot-reloading system
|
// hot-reloading system
|
||||||
log.info('API hot-reloading is active.')
|
log.info('API hot-reloading is active.')
|
||||||
const chokidar = require('chokidar')
|
const chokidar = require('chokidar')
|
||||||
|
const path = require('path')
|
||||||
let hotMiddleware = mw
|
let hotMiddleware = mw
|
||||||
|
|
||||||
this.__apiWatcher = chokidar.watch('api/**')
|
this.__apiWatcher = chokidar.watch(path.join(__dirname, 'api/**'))
|
||||||
this.__apiWatcher.on('all', async (path) => {
|
this.__apiWatcher.on('all', async (path) => {
|
||||||
log.info('reloading APIs...', path)
|
log.info('reloading APIs...', path)
|
||||||
hotMiddleware = await this.loadRoutes(true)
|
hotMiddleware = await this.loadRoutes(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.__rpcWatcher = chokidar.watch('rpc/**')
|
this.__rpcWatcher = chokidar.watch(path.join(__dirname, 'rpc/**'))
|
||||||
this.__rpcWatcher.on('all', (path) => {
|
this.__rpcWatcher.on('all', (path) => {
|
||||||
log.info('reloading RPCs...', path)
|
log.info('reloading RPCs...', path)
|
||||||
this.ctx.RPC.reload()
|
this.ctx.RPC.reload()
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
// @flow
|
// @flow
|
||||||
import logger from '../logger'
|
|
||||||
import glob from 'glob'
|
import glob from 'glob'
|
||||||
|
|
||||||
import type { Router, AppContext } from '../Roleypoly'
|
import type { Router, AppContext } from '../Roleypoly'
|
||||||
|
import path from 'path'
|
||||||
|
import logger from '../logger'
|
||||||
const log = logger(__filename)
|
const log = logger(__filename)
|
||||||
|
|
||||||
const PROD = process.env.NODE_ENV === 'production'
|
const PROD = process.env.NODE_ENV === 'production'
|
||||||
|
|
||||||
export default async (router: Router, ctx: AppContext, { forceClear = false }: { forceClear: boolean } = {}) => {
|
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)
|
log.debug('found apis', apis)
|
||||||
|
|
||||||
for (let a of apis) {
|
for (let a of apis) {
|
||||||
|
@ -19,12 +18,11 @@ export default async (router: Router, ctx: AppContext, { forceClear = false }: {
|
||||||
}
|
}
|
||||||
log.debug(`mounting ${a}`)
|
log.debug(`mounting ${a}`)
|
||||||
try {
|
try {
|
||||||
const pathname = a.replace('api/', '')
|
|
||||||
if (forceClear) {
|
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.
|
// $FlowFixMe this isn't an important error. potentially dangerous, but irrelevant.
|
||||||
require(pathname).default(router, ctx)
|
require(a).default(router, ctx)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(`couldn't mount ${a}`, e)
|
log.error(`couldn't mount ${a}`, e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,18 +15,18 @@ export type Models = {
|
||||||
|
|
||||||
export default (sql: Sequelize): Models => {
|
export default (sql: Sequelize): Models => {
|
||||||
const models: 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)
|
log.debug('found models', modelFiles)
|
||||||
|
|
||||||
modelFiles.forEach((v) => {
|
modelFiles.forEach((v) => {
|
||||||
let name = path.basename(v).replace('.js', '')
|
let name = path.basename(v).replace('.js', '')
|
||||||
if (v === './models/index.js') {
|
if (v === `./index.js`) {
|
||||||
log.debug('index.js hit, skipped')
|
log.debug('index.js hit, skipped')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
log.debug('importing..', v.replace('models/', ''))
|
log.debug('importing..', v)
|
||||||
let model = sql.import(v.replace('models/', ''))
|
let model = sql.import(v)
|
||||||
models[name] = model
|
models[name] = model
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.fatal('error importing model ' + v, err)
|
log.fatal('error importing model ' + v, err)
|
||||||
|
|
|
@ -11,14 +11,14 @@ export default (ctx: AppContext, forceClear: ?boolean = false): {
|
||||||
[rpc: string]: Function
|
[rpc: string]: Function
|
||||||
} => {
|
} => {
|
||||||
let map = {}
|
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)
|
log.debug('found rpcs', apis)
|
||||||
|
|
||||||
for (let a of apis) {
|
for (let a of apis) {
|
||||||
const filename = path.basename(a)
|
const filename = path.basename(a)
|
||||||
const dirname = path.dirname(a)
|
const dirname = path.dirname(a)
|
||||||
|
|
||||||
const pathname = a.replace('rpc/', '')
|
const pathname = a
|
||||||
delete require.cache[require.resolve(pathname)]
|
delete require.cache[require.resolve(pathname)]
|
||||||
|
|
||||||
// internal stuff
|
// internal stuff
|
||||||
|
|
|
@ -95,12 +95,12 @@ export const member = (
|
||||||
$: AppContext,
|
$: AppContext,
|
||||||
fn: (ctx: Context, server: string, ...args: any[]) => any,
|
fn: (ctx: Context, server: string, ...args: any[]) => any,
|
||||||
silent: boolean = false
|
silent: boolean = false
|
||||||
) => authed($, (
|
) => authed($, async (
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
server: string,
|
server: string,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) => {
|
) => {
|
||||||
if ($.discord.isMember(server, ctx.session.userId)) {
|
if (await $.discord.isMember(server, ctx.session.userId)) {
|
||||||
return fn(ctx, server, ...args)
|
return fn(ctx, server, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { type AppContext } from '../Roleypoly'
|
||||||
import { type Context } from 'koa'
|
import { type Context } from 'koa'
|
||||||
import { type Guild } from 'eris'
|
import { type Guild } from 'eris'
|
||||||
import * as secureAs from './_security'
|
import * as secureAs from './_security'
|
||||||
|
import RPCError from './_error'
|
||||||
|
|
||||||
export default ($: AppContext) => ({
|
export default ($: AppContext) => ({
|
||||||
|
|
||||||
|
@ -24,23 +25,18 @@ export default ($: AppContext) => ({
|
||||||
return $.P.serverSlug(srv)
|
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 { userId } = (ctx.session: { userId: string })
|
||||||
|
|
||||||
const srv = $.discord.client.guilds.get(id)
|
const srv = await $.discord.fetcher.getGuild(id)
|
||||||
if (srv == null) {
|
if (srv == null) {
|
||||||
return { err: 'not_found' }
|
throw new RPCError('server not found', 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
let gm
|
let gm = await $.discord.gm(id, userId, { canFake: true })
|
||||||
if (srv.members.has(userId)) {
|
|
||||||
gm = $.discord.gm(id, userId)
|
|
||||||
} else if ($.discord.isRoot(userId)) {
|
|
||||||
gm = $.discord.fakeGm({ id: userId })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gm == null) {
|
if (gm == null) {
|
||||||
return { err: 'not_found' }
|
throw new RPCError('server not found', 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.P.presentableServer(srv, gm)
|
return $.P.presentableServer(srv, gm)
|
||||||
|
|
|
@ -2,33 +2,71 @@
|
||||||
import type { IFetcher } from './types'
|
import type { IFetcher } from './types'
|
||||||
import type DiscordSvc from '../discord'
|
import type DiscordSvc from '../discord'
|
||||||
import type ErisClient, { User, Member, Guild } from 'eris'
|
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 {
|
export default class BotFetcher implements IFetcher {
|
||||||
ctx: DiscordSvc
|
ctx: DiscordSvc
|
||||||
client: ErisClient
|
client: ErisClient
|
||||||
|
cache: LRU<string, Guild | Member | User>
|
||||||
|
|
||||||
constructor (ctx: DiscordSvc) {
|
constructor (ctx: DiscordSvc) {
|
||||||
this.ctx = ctx
|
this.ctx = ctx
|
||||||
this.client = ctx.client
|
this.client = ctx.client
|
||||||
|
this.cache = new LRU({
|
||||||
|
max: 50,
|
||||||
|
maxAge: 1000 * 60 * 10
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser = async (id: string): Promise<?User> => {
|
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 {
|
try {
|
||||||
return await this.client.getRESTUser(id)
|
const u = await this.client.getRESTUser(id)
|
||||||
|
this.cache.set(`U:${id}`, u)
|
||||||
|
return u
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getMember = async (server: string, user: string): Promise<?Member> => {
|
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 {
|
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) {
|
} catch (e) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getGuild = async (server: string): Promise<?Guild> => {
|
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 {
|
try {
|
||||||
return await this.client.getRESTGuild(server)
|
const g = await this.client.getRESTGuild(server)
|
||||||
|
this.cache.set(`G:${server}`, g)
|
||||||
|
return g
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,41 +4,18 @@ import LRU from 'lru-cache'
|
||||||
import { type AppContext } from '../Roleypoly'
|
import { type AppContext } from '../Roleypoly'
|
||||||
import { type Models } from '../models'
|
import { type Models } from '../models'
|
||||||
import { type ServerModel } from '../models/Server'
|
import { type ServerModel } from '../models/Server'
|
||||||
import type DiscordService, { Permissions } from './discord'
|
import type DiscordService from './discord'
|
||||||
import {
|
import {
|
||||||
type Guild,
|
type Guild,
|
||||||
type Member,
|
|
||||||
type Collection
|
type Collection
|
||||||
} from 'eris'
|
} from 'eris'
|
||||||
|
import type {
|
||||||
|
Member,
|
||||||
|
PresentableServer,
|
||||||
|
ServerSlug,
|
||||||
|
PresentableRole
|
||||||
|
} from '@roleypoly/types'
|
||||||
import areduce from '../util/areduce'
|
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 {
|
class PresentationService extends Service {
|
||||||
cache: LRU
|
cache: LRU
|
||||||
M: Models
|
M: Models
|
||||||
|
@ -61,8 +38,8 @@ class PresentationService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
presentableServers (collection: Collection<string, Guild>, userId: string) {
|
presentableServers (collection: Collection<Guild>, userId: string) {
|
||||||
return areduce(collection.array(), async (acc, server) => {
|
return areduce(Array.from(collection.values()), async (acc, server) => {
|
||||||
const gm = server.members.get(userId)
|
const gm = server.members.get(userId)
|
||||||
if (gm == null) {
|
if (gm == null) {
|
||||||
throw new Error(`somehow this guildmember ${userId} of ${server.id} didn't exist.`)
|
throw new Error(`somehow this guildmember ${userId} of ${server.id} didn't exist.`)
|
||||||
|
@ -79,12 +56,12 @@ class PresentationService extends Service {
|
||||||
return {
|
return {
|
||||||
id: server.id,
|
id: server.id,
|
||||||
gm: {
|
gm: {
|
||||||
nickname: gm.nickname || gm.user.username,
|
nickname: gm.nick || gm.user.username,
|
||||||
color: gm.displayHexColor,
|
color: gm?.color,
|
||||||
roles: gm.roles.keyArray()
|
roles: gm.roles
|
||||||
},
|
},
|
||||||
server: this.serverSlug(server),
|
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,
|
message: sd.message,
|
||||||
categories: sd.categories,
|
categories: sd.categories,
|
||||||
perms: this.discord.getPermissions(gm)
|
perms: this.discord.getPermissions(gm)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { addAwaitOutsideToReplServer } from 'await-outside'
|
||||||
import Roleypoly from '../Roleypoly'
|
import Roleypoly from '../Roleypoly'
|
||||||
import chokidar from 'chokidar'
|
import chokidar from 'chokidar'
|
||||||
import logger from '../logger'
|
import logger from '../logger'
|
||||||
process.env.DEBUG = false
|
// process.env.DEBUG = false
|
||||||
process.env.IS_BOT = false
|
process.env.IS_BOT = false
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
|
@ -20,7 +20,7 @@ export type ServerModel = {
|
||||||
export type PresentableServer = ServerModel & {
|
export type PresentableServer = ServerModel & {
|
||||||
id: string,
|
id: string,
|
||||||
gm?: {
|
gm?: {
|
||||||
color: number | string,
|
color?: number | string,
|
||||||
nickname: string,
|
nickname: string,
|
||||||
roles: string[]
|
roles: string[]
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const next = require('next')
|
const next = require('next')
|
||||||
|
|
||||||
const connector = ({ dev }) => {
|
const connector = ({ dev }) => {
|
||||||
return next({ dev })
|
return next({ dev, dir: __dirname })
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = connector
|
module.exports = connector
|
||||||
|
|
Loading…
Add table
Reference in a new issue