first sync

This commit is contained in:
41666 2017-12-04 21:25:13 -06:00
parent 5d6f382c8a
commit a4acc441ea
52 changed files with 28315 additions and 0 deletions

21
Server/.editorconfig Normal file
View file

@ -0,0 +1,21 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

6
Server/.env Normal file
View file

@ -0,0 +1,6 @@
DB_URL=postgres://roleypoly:19216801@localhost:5432/roleypoly
DEBUG=true
NODE_ENV=development
DISCORD_CLIENT_ID=363916474127220739
DISCORD_CLIENT_SECRET=PZd3u4Rkok200o-5UcG3hnB8MVdALPz5
APP_URL=http://localhost:6769

2
Server/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
.data

53
Server/Roleypoly.js Normal file
View file

@ -0,0 +1,53 @@
const log = new (require('./logger'))('World')
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 = {}
this.ctx.config = {
appUrl: process.env.APP_URL
}
this.ctx.io = io
this.__app = app
if (log.debugOn) log.warn('debug mode is on')
this.__initialized = this._mountServices()
}
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()
// this.ctx.redis = new (require('ioredis'))({
// port: process.env.REDIS_PORT || '6379',
// host: process.env.REDIS_HOST || 'localhost',
// parser: 'hiredis',
// dropBufferSupport: true,
// enableReadyCheck: true,
// enableOfflineQueue: true
// })
this.ctx.discord = new (require('./services/discord'))(this.ctx)
}
async mountRoutes () {
fetchApis(this.router, this.ctx)
this.__app.use(this.router.middleware())
}
}
module.exports = Roleypoly

3
Server/api/auth.js Normal file
View file

@ -0,0 +1,3 @@
module.exports = R => {
}

20
Server/api/index.js Normal file
View file

@ -0,0 +1,20 @@
const log = new (require('../logger'))('api/index')
const glob = require('glob')
module.exports = async (router, ctx) => {
const apis = glob.sync('./api/**/!(index).js')
log.debug('found apis', apis)
for (let a of apis) {
if (a.endsWith('_test.js') !== null && process.env.NODE_ENV !== 'development') {
log.debug(`skipping ${a}`)
continue
}
log.debug(`mounting ${a}`)
try {
require(a.replace('api/', ''))(router, ctx)
} catch (e) {
log.error(`couldn't mount ${a}`, e)
}
}
}

6
Server/api/servers.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = (R, $) => {
R.get('/api/servers', (ctx) => {
ctx.body = 'hi'
console.log($)
})
}

44
Server/index.js Normal file
View file

@ -0,0 +1,44 @@
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 router = require('koa-better-router')().loadMethods()
const Roleypoly = require('./Roleypoly')
// Create the server and socket.io server
const server = http.createServer(app.callback())
const io = _io(server, { transports: ['websocket'], path: '/api/socket.io', wsEngine: 'uws' })
const M = new Roleypoly(router, io, app) // eslint-disable-line no-unused-vars
async function start () {
// body parser
const bodyParser = require('koa-bodyparser')
app.use(bodyParser({ types: ['json'] }))
// Request logger
app.use(async (ctx, next) => {
let timeStart = new Date()
await next()
let timeElapsed = new Date() - timeStart
log.request(`${ctx.status} ${ctx.method} ${ctx.url} - ${ctx.ip} - took ${timeElapsed}ms`)
return null
})
// Construct the Roleypoly!
await M.mountRoutes()
// Start it!
await M.awaitServices()
log.info(`starting HTTP server on ${process.env.APP_PORT || 6769}`)
server.listen(process.env.APP_PORT || 6769)
}
start().catch(e => {
console.trace(e)
})

56
Server/logger.js Normal file
View file

@ -0,0 +1,56 @@
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
}
fatal (text, ...data) {
this.error(text, data)
if (typeof data[data.length - 1] === 'number') {
process.exit(data[data.length - 1])
}
throw text
}
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)
}
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)
}
request (text, ...data) {
console.info(chalk.green.bold(`HTTP ${this.name}:`) + `\n ${text}`)
}
debug (text, ...data) {
if (this.debugOn) {
console.log(chalk.gray.bold(`DEBUG ${this.name}:`) + `\n ${text}`, data)
}
}
sql (logger, ...data) {
if (logger.debugOn) {
console.log(chalk.bold('DEBUG SQL:\n '), data)
}
}
}
module.exports = Logger

14
Server/models/Server.js Normal file
View file

@ -0,0 +1,14 @@
module.exports = (sql, DataTypes) => {
return sql.define('server', {
id: { // discord snowflake
type: DataTypes.TEXT,
primaryKey: true
},
categories: {
type: DataTypes.JSON
},
note: {
type: DataTypes.TEXT
}
})
}

7
Server/models/Session.js Normal file
View file

@ -0,0 +1,7 @@
module.exports = (sequelize, DataTypes) => {
return sequelize.define('session', {
id: { type: DataTypes.TEXT, primaryKey: true },
maxAge: DataTypes.BIGINT,
data: DataTypes.JSONB
})
}

37
Server/models/index.js Normal file
View file

@ -0,0 +1,37 @@
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)
modelFiles.forEach((v) => {
let name = path.basename(v).replace('.js', '')
if (v === './models/index.js') {
log.debug('index.js hit, skipped')
return
}
try {
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)
}
})
Object.keys(models).forEach((v) => {
if (models[v].hasOwnProperty('__associations')) {
models[v].__associations(models)
}
if (models[v].hasOwnProperty('__instanceMethods')) {
models[v].__instanceMethods(models[v])
}
})
return models
}

4669
Server/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

40
Server/package.json Normal file
View file

@ -0,0 +1,40 @@
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "standard && node index.js",
"fix": "standard --fix",
"dev": "pm2 start index.js --watch",
"pm2": "pm2"
},
"dependencies": {
"chalk": "^2.3.0",
"commander": "^2.12.2",
"discord.js": "^11.2.1",
"dotenv": "^4.0.0",
"erlpack": "github:discordapp/erlpack",
"eventemitter3": "^3.0.0",
"glob": "^7.1.2",
"immutable": "^3.8.2",
"inquirer": "^4.0.1",
"koa": "^2.4.1",
"koa-better-router": "^2.1.1",
"koa-bodyparser": "^4.2.0",
"koa-passport": "^4.0.1",
"koa-session": "^5.5.1",
"ksuid": "^0.4.0",
"passport-discord": "^0.1.3",
"passport-oauth2-refresh": "^1.0.0",
"pg": "^7.4.0",
"pg-hstore": "^2.3.2",
"pm2": "^2.8.0",
"sequelize": "^4.25.1",
"socket.io": "^2.0.4",
"standard": "^10.0.3",
"superagent": "^3.8.1",
"uuid": "^3.1.0",
"uws": "^9.14.0",
"yargs": "^10.0.3"
}
}

View file

@ -0,0 +1,10 @@
const Logger = require('../logger')
class Service {
constructor (ctx) {
this.ctx = ctx
this.log = new Logger(this.constructor.name)
}
}
module.exports = Service

View file

@ -0,0 +1,80 @@
const Service = require('./Service')
const discord = require('discord.js')
const superagent = require('superagent')
class DiscordService extends Service {
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}/oauth/bot/callback`
this.startBot()
}
async startBot () {
await discord.login(this.botToken)
}
// oauth step 2 flow, grab the auth token via code
async getAuthToken (code) {
const url = 'https://discordapp.com/api/oauth2/token'
try {
const rsp =
await superagent
.post(url)
.send({
client_id: this.clientId,
client_secret: this.clientSecret,
grant_type: 'authorization_code',
code: code,
redirect_uri: this.oauthCallback
})
return rsp.body
} catch (e) {
this.log.error('getAuthToken failed', e)
throw e
}
}
// on sign out, we revoke the token we had.
async revokeAuthToken (code, state) {
const url = 'https://discordapp.com/api/oauth2/revoke'
try {
const rsp =
await superagent
.post(url)
.send({
client_id: this.clientId,
client_secret: this.clientSecret,
grant_type: 'authorization_code',
code: code,
redirect_uri: this.oauthCallback
})
return rsp.body
} catch (e) {
this.log.error('getAuthToken failed', e)
throw e
}
}
// 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`
}
// 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&redirect_uri=${this.botCallback}`
}
}
module.exports = DiscordService

View file

@ -0,0 +1,10 @@
const ksuid = require('ksuid')
module.exports = {
ksuid (field = 'id') {
return async function () {
this.id = await ksuid.random()
return this
}
}
}

3294
Server/yarn.lock Normal file

File diff suppressed because it is too large Load diff