From d2aecb38ca6e01d8fa273df0a82b6a7cfcea2e62 Mon Sep 17 00:00:00 2001 From: Kata Date: Sun, 10 Mar 2019 03:18:11 -0500 Subject: [PATCH] flowtyped everything, some functional, safety, and structural changes --- .babelrc | 16 +- .eslintrc.json | 4 - .gitignore | 4 +- Dockerfile | 2 +- Roleypoly.js | 134 +- UI/.babelrc | 5 + UI/components/global-colors.js | 17 +- UI/components/header/auth.js | 11 + UI/components/header/common.js | 14 + UI/components/header/unauth.js | 11 + UI/components/social-cards.js | 36 + UI/config/redux.js | 15 + UI/config/rpc.js | 4 + UI/containers/header-bar.js | 30 + UI/containers/user.js | 32 + UI/pages/_app.js | 22 +- UI/pages/_internal/_server.js | 24 +- UI/pages/index.js | 22 +- UI/pages/testrpc.js | 24 + UI/rpc/index.js | 106 + UI/types.js | 8 + api/auth.js | 30 +- api/index.js | 14 +- api/servers.js | 84 +- api/servers_test.js | 35 +- api/ui.js | 37 +- babel.config.js | 6 + flow-typed/next.js | 35 + flow-typed/npm/@babel/cli_vx.x.x.js | 87 + flow-typed/npm/@babel/node_vx.x.x.js | 46 + flow-typed/npm/@babel/preset-env_vx.x.x.js | 144 + flow-typed/npm/@babel/preset-flow_vx.x.x.js | 32 + flow-typed/npm/@discordjs/uws_vx.x.x.js | 32 + flow-typed/npm/@primer/components_vx.x.x.js | 403 + flow-typed/npm/babel-eslint_vx.x.x.js | 109 + ...lugin-transform-flow-strip-types_vx.x.x.js | 32 + flow-typed/npm/babel-preset-flow_vx.x.x.js | 32 + flow-typed/npm/babel-register_v6.x.x.js | 44 + flow-typed/npm/chalk_v2.x.x.js | 98 + flow-typed/npm/chokidar_vx.x.x.js | 45 + flow-typed/npm/discord.js_v11.3.x.js | 1871 +++++ flow-typed/npm/dotenv_vx.x.x.js | 53 + flow-typed/npm/erlpack_vx.x.x.js | 46 + .../npm/eslint-plugin-flowtype_vx.x.x.js | 459 + flow-typed/npm/fast-redux_vx.x.x.js | 123 + flow-typed/npm/flow-bin_v0.x.x.js | 6 + flow-typed/npm/flow-typed_vx.x.x.js | 193 + flow-typed/npm/fnv-plus_vx.x.x.js | 38 + flow-typed/npm/glob_v7.1.x.js | 87 + flow-typed/npm/invariant_v2.x.x.js | 6 + flow-typed/npm/keygrip_vx.x.x.js | 33 + flow-typed/npm/koa-better-router_vx.x.x.js | 38 + flow-typed/npm/koa-bodyparser_v4.x.x.js | 28 + flow-typed/npm/koa-compress_vx.x.x.js | 33 + flow-typed/npm/koa-session_vx.x.x.js | 52 + flow-typed/npm/koa_v2.x.x.js | 319 + flow-typed/npm/kompression_vx.x.x.js | 32 + flow-typed/npm/ksuid_vx.x.x.js | 38 + flow-typed/npm/lru-cache_vx.x.x.js | 33 + flow-typed/npm/next-redux-wrapper_vx.x.x.js | 74 + flow-typed/npm/next_vx.x.x.js | 564 ++ flow-typed/npm/pg-hstore_vx.x.x.js | 53 + flow-typed/npm/pg_v7.x.x.js | 309 + flow-typed/npm/pm2_vx.x.x.js | 472 ++ flow-typed/npm/primer_vx.x.x.js | 32 + flow-typed/npm/react-redux_vx.x.x.js | 375 + .../npm/redux-devtools-extension_v2.x.x.js | 127 + flow-typed/npm/redux-thunk_vx.x.x.js | 60 + flow-typed/npm/redux_v4.x.x.js | 59 + flow-typed/npm/sequelize_v4.x.x.js | 7454 +++++++++++++++++ flow-typed/npm/socket.io_vx.x.x.js | 60 + flow-typed/npm/standard-flow_vx.x.x.js | 59 + flow-typed/npm/standard_vx.x.x.js | 45 + flow-typed/npm/superagent_vx.x.x.js | 158 + flow-typed/npm/uuid_v3.x.x.js | 102 + index.js | 45 +- logger.js | 42 +- models/Server.js | 20 +- models/index.js | 22 +- package.json | 56 +- rpc/_autoloader.js | 60 + rpc/_error.js | 15 + rpc/index.js | 111 + rpc/rpc_test.js | 14 + rpc/servers.js | 15 + services/Service.js | 12 +- services/discord.js | 123 +- services/presentation.js | 68 +- services/server.js | 39 +- services/sessions.js | 23 +- util/areduce.js | 8 + yarn.lock | 2944 ++++--- 92 files changed, 17554 insertions(+), 1440 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 UI/.babelrc create mode 100644 UI/components/header/auth.js create mode 100644 UI/components/header/common.js create mode 100644 UI/components/header/unauth.js create mode 100644 UI/components/social-cards.js create mode 100644 UI/config/redux.js create mode 100644 UI/config/rpc.js create mode 100644 UI/containers/header-bar.js create mode 100644 UI/containers/user.js create mode 100644 UI/pages/testrpc.js create mode 100644 UI/rpc/index.js create mode 100644 UI/types.js create mode 100644 babel.config.js create mode 100644 flow-typed/next.js create mode 100644 flow-typed/npm/@babel/cli_vx.x.x.js create mode 100644 flow-typed/npm/@babel/node_vx.x.x.js create mode 100644 flow-typed/npm/@babel/preset-env_vx.x.x.js create mode 100644 flow-typed/npm/@babel/preset-flow_vx.x.x.js create mode 100644 flow-typed/npm/@discordjs/uws_vx.x.x.js create mode 100644 flow-typed/npm/@primer/components_vx.x.x.js create mode 100644 flow-typed/npm/babel-eslint_vx.x.x.js create mode 100644 flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x.js create mode 100644 flow-typed/npm/babel-preset-flow_vx.x.x.js create mode 100644 flow-typed/npm/babel-register_v6.x.x.js create mode 100644 flow-typed/npm/chalk_v2.x.x.js create mode 100644 flow-typed/npm/chokidar_vx.x.x.js create mode 100644 flow-typed/npm/discord.js_v11.3.x.js create mode 100644 flow-typed/npm/dotenv_vx.x.x.js create mode 100644 flow-typed/npm/erlpack_vx.x.x.js create mode 100644 flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js create mode 100644 flow-typed/npm/fast-redux_vx.x.x.js create mode 100644 flow-typed/npm/flow-bin_v0.x.x.js create mode 100644 flow-typed/npm/flow-typed_vx.x.x.js create mode 100644 flow-typed/npm/fnv-plus_vx.x.x.js create mode 100644 flow-typed/npm/glob_v7.1.x.js create mode 100644 flow-typed/npm/invariant_v2.x.x.js create mode 100644 flow-typed/npm/keygrip_vx.x.x.js create mode 100644 flow-typed/npm/koa-better-router_vx.x.x.js create mode 100644 flow-typed/npm/koa-bodyparser_v4.x.x.js create mode 100644 flow-typed/npm/koa-compress_vx.x.x.js create mode 100644 flow-typed/npm/koa-session_vx.x.x.js create mode 100644 flow-typed/npm/koa_v2.x.x.js create mode 100644 flow-typed/npm/kompression_vx.x.x.js create mode 100644 flow-typed/npm/ksuid_vx.x.x.js create mode 100644 flow-typed/npm/lru-cache_vx.x.x.js create mode 100644 flow-typed/npm/next-redux-wrapper_vx.x.x.js create mode 100644 flow-typed/npm/next_vx.x.x.js create mode 100644 flow-typed/npm/pg-hstore_vx.x.x.js create mode 100644 flow-typed/npm/pg_v7.x.x.js create mode 100644 flow-typed/npm/pm2_vx.x.x.js create mode 100644 flow-typed/npm/primer_vx.x.x.js create mode 100644 flow-typed/npm/react-redux_vx.x.x.js create mode 100644 flow-typed/npm/redux-devtools-extension_v2.x.x.js create mode 100644 flow-typed/npm/redux-thunk_vx.x.x.js create mode 100644 flow-typed/npm/redux_v4.x.x.js create mode 100644 flow-typed/npm/sequelize_v4.x.x.js create mode 100644 flow-typed/npm/socket.io_vx.x.x.js create mode 100644 flow-typed/npm/standard-flow_vx.x.x.js create mode 100644 flow-typed/npm/standard_vx.x.x.js create mode 100644 flow-typed/npm/superagent_vx.x.x.js create mode 100644 flow-typed/npm/uuid_v3.x.x.js create mode 100644 rpc/_autoloader.js create mode 100644 rpc/_error.js create mode 100644 rpc/index.js create mode 100644 rpc/rpc_test.js create mode 100644 rpc/servers.js create mode 100644 util/areduce.js diff --git a/.babelrc b/.babelrc index 2507b5a..1d42ce1 100644 --- a/.babelrc +++ b/.babelrc @@ -1,8 +1,14 @@ { - "presets": [ - "next/babel" - ], + "presets": [ ["@babel/preset-env", { + "targets": { + "node": true + } + }], "@babel/preset-flow" ], "plugins": [ - "transform-flow-strip-types" - ] + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-proposal-class-properties", + ["@babel/plugin-transform-runtime", + { "helpers": false }] + ], + "ignore": ["ui/**/*"] } diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index b4a10d9..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": [ "standard" ], - "parser": "babel-eslint" -} diff --git a/.gitignore b/.gitignore index 23f699f..f5de1ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ .env *.env - +dist /docker-compose.test.yml node_modules .vscode .data + +yarn-error\.log diff --git a/Dockerfile b/Dockerfile index a677dd1..1808301 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,4 +10,4 @@ FROM mhart/alpine-node:10 ENV NODE_ENV production WORKDIR /dist COPY --from=builder /src /dist -CMD node index.js +CMD npm start diff --git a/Roleypoly.js b/Roleypoly.js index 61b8f71..c8d1c99 100644 --- a/Roleypoly.js +++ b/Roleypoly.js @@ -1,29 +1,94 @@ -const log = new (require('./logger'))('Roleypoly') -const Sequelize = require('sequelize') -const fetchModels = require('./models') -const fetchApis = require('./api') -const Next = require('next') -const betterRouter = require('koa-better-router') +// @flow +import Sequelize from 'sequelize' +import Next from 'next' +import betterRouter from 'koa-better-router' +import EventEmitter from 'events' +import fs from 'fs' +import logger from './logger' +import ServerService from './services/server' +import DiscordService from './services/discord' +import SessionService from './services/sessions' +import PresentationService from './services/presentation' +import RPCServer from './rpc' +import fetchModels, { type Models } from './models' +import fetchApis from './api' + +import type SocketIO from 'socket.io' +import type KoaApp, { Context } from 'koa' + +const log = logger(__filename) + +type HTTPHandler = (path: string, handler: (ctx: Context, next: () => void) => any) => void +export type Router = { + get: HTTPHandler, + post: HTTPHandler, + patch: HTTPHandler, + delete: HTTPHandler, + put: HTTPHandler, + middleware: () => any +} +export type AppContext = { + config: { + appUrl: string, + dev: boolean, + hotReload: boolean + }, + ui: Next, + uiHandler: Next.Handler, + io: SocketIO, + + server: ServerService, + discord: DiscordService, + sessions: SessionService, + P: PresentationService, + RPC: RPCServer, + M: Models, + sql: Sequelize +} class Roleypoly { - constructor (io, app) { + ctx: AppContext|any + io: SocketIO + router: Router + + M: Models + + __app: KoaApp + __initialized: Promise + __apiWatcher: EventEmitter + __rpcWatcher: EventEmitter + constructor (io: SocketIO, app: KoaApp) { this.io = io - this.ctx = {} - - this.ctx.config = { - appUrl: process.env.APP_URL, - dev: process.env.NODE_ENV !== 'production', - hotReload: process.env.NO_HOT_RELOAD !== '1' - } - - this.ctx.io = io this.__app = app if (log.debugOn) log.warn('debug mode is on') const dev = process.env.NODE_ENV !== 'production' - this.ctx.ui = Next({ dev, dir: './ui' }) - this.ctx.uiHandler = this.ctx.ui.getRequestHandler() + + // simple check if we're in a compiled situation or not. + let uiDir = './ui' + if (!fs.existsSync(uiDir) && fs.existsSync('../ui')) { + uiDir = '../ui' + } + + const ui = Next({ dev, dir: uiDir }) + const uiHandler = ui.getRequestHandler() + + const appUrl = process.env.APP_URL + if (appUrl == null) { + throw new Error('APP_URL was unset.') + } + + this.ctx = { + config: { + appUrl, + dev, + hotReload: process.env.NO_HOT_RELOAD !== '1' + }, + io, + ui, + uiHandler + } this.__initialized = this._mountServices() } @@ -33,31 +98,30 @@ class Roleypoly { } async _mountServices () { - const sequelize = new Sequelize(process.env.DB_URL, { logging: log.sql.bind(log, log) }) + const dbUrl: ?string = process.env.DB_URL + if (dbUrl == null) { + throw log.fatal('DB_URL not set.') + } + + const sequelize = new Sequelize(dbUrl, { 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.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 ServerService(this.ctx) + this.ctx.discord = new DiscordService(this.ctx) + this.ctx.sessions = new SessionService(this.ctx) + this.ctx.P = new PresentationService(this.ctx) + this.ctx.RPC = new RPCServer(this) } - async loadRoutes (forceClear = false) { + 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) // after routing, add the * for ui handler this.router.get('*', async ctx => { @@ -83,6 +147,12 @@ class Roleypoly { hotMiddleware = await this.loadRoutes(true) }) + this.__rpcWatcher = chokidar.watch('rpc/**') + this.__rpcWatcher.on('all', (path) => { + log.info('reloading RPCs...', path) + this.ctx.RPC.reload() + }) + // custom passthrough so we use a specially scoped middleware. mw = (ctx, next) => { return hotMiddleware(ctx, next) diff --git a/UI/.babelrc b/UI/.babelrc new file mode 100644 index 0000000..571c6da --- /dev/null +++ b/UI/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "next/babel", "@babel/preset-flow" + ] +} diff --git a/UI/components/global-colors.js b/UI/components/global-colors.js index 8d3f5de..938e9f2 100644 --- a/UI/components/global-colors.js +++ b/UI/components/global-colors.js @@ -1,3 +1,6 @@ +// @flow +import * as React from 'react' + export const colors = { white: '#efefef', c9: '#EBD6D4', @@ -12,7 +15,7 @@ export const colors = { } const getColors = () => { - Object.keys(colors).map(key => { + return Object.keys(colors).map(key => { const nk = key.replace(/c([0-9])/, '$1') return `--c-${nk}: ${colors[key]};` }).join(' \n') @@ -26,6 +29,18 @@ body { text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + /* prevent FOUC */ + transition: opacity 0.2s ease-in-out; + opacity: 0; +} + +.wf-active body { + opacity: 1; +} + +// FOUC guard if we take too long +.force-active body { + opacity: 1; } .font-sans-serif { diff --git a/UI/components/header/auth.js b/UI/components/header/auth.js new file mode 100644 index 0000000..a265421 --- /dev/null +++ b/UI/components/header/auth.js @@ -0,0 +1,11 @@ +// @flow +import * as React from 'react' +import HeaderBarCommon from './common' + +const HeaderBarAuth: React.StatelessFunctionalComponent<{}> = () => ( + + hi + +) + +export default HeaderBarAuth diff --git a/UI/components/header/common.js b/UI/components/header/common.js new file mode 100644 index 0000000..f80c712 --- /dev/null +++ b/UI/components/header/common.js @@ -0,0 +1,14 @@ +// @flow +import * as React from 'react' + +export type CommonProps = { + children: React.Element +} + +const HeaderBarCommon: React.StatelessFunctionalComponent = ({ children }) => ( +
+ { children } +
+) + +export default HeaderBarCommon diff --git a/UI/components/header/unauth.js b/UI/components/header/unauth.js new file mode 100644 index 0000000..0155a84 --- /dev/null +++ b/UI/components/header/unauth.js @@ -0,0 +1,11 @@ +// @flow +import * as React from 'react' +import HeaderBarCommon from './common' + +const HeaderBarUnauth: React.StatelessFunctionalComponent<{}> = () => ( + + hi + +) + +export default HeaderBarUnauth diff --git a/UI/components/social-cards.js b/UI/components/social-cards.js new file mode 100644 index 0000000..1a72cd7 --- /dev/null +++ b/UI/components/social-cards.js @@ -0,0 +1,36 @@ +// @flow +import * as React from 'react' +import NextHead from 'next/head' + +export type SocialCardProps = { + title?: string, + description?: string, + image?: string, + imageSize?: number +} + +const defaultProps: SocialCardProps = { + title: 'Roleypoly', + description: 'Tame your Discord roles.', + image: 'https://rp.kat.cafe/static/social.png', + imageSize: 200 +} + +const SocialCards: React.StatelessFunctionalComponent = (props) => { + props = { + ...defaultProps, + ...props + } + + return + + + + + + + + +} + +export default SocialCards diff --git a/UI/config/redux.js b/UI/config/redux.js new file mode 100644 index 0000000..dd43cb3 --- /dev/null +++ b/UI/config/redux.js @@ -0,0 +1,15 @@ +import { createStore, applyMiddleware } from 'redux' +import { composeWithDevTools } from 'redux-devtools-extension' +import thunkMiddleware from 'redux-thunk' +import withNextRedux from 'next-redux-wrapper' +import { rootReducer } from 'fast-redux' + +export const initStore = (initialState = {}) => { + return createStore( + rootReducer, + initialState, + composeWithDevTools(applyMiddleware(thunkMiddleware)) + ) +} + +export const withRedux = (comp) => withNextRedux(initStore)(comp) diff --git a/UI/config/rpc.js b/UI/config/rpc.js new file mode 100644 index 0000000..707e231 --- /dev/null +++ b/UI/config/rpc.js @@ -0,0 +1,4 @@ +// @flow +import RPCClient from '../rpc' + +export default (new RPCClient({ forceDev: false })).rpc diff --git a/UI/containers/header-bar.js b/UI/containers/header-bar.js new file mode 100644 index 0000000..8a751f3 --- /dev/null +++ b/UI/containers/header-bar.js @@ -0,0 +1,30 @@ +// @flow +import * as React from 'react' +import dynamic from 'next/dynamic' +import { withRedux } from '../config/redux' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { namespaceConfig } from 'fast-redux' +import * as User from './user' + +type Props = { + user: User.User +} + +const HeaderBarAuth = dynamic(() => import('../components/header/auth')) +const HeaderBarUnauth = dynamic(() => import('../components/header/unauth')) + +const HeaderBar: React.StatelessFunctionalComponent = () => { + // if () + return null +} + +const mapStateToProps = (state): Props => { + return {} +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({ }, dispatch) +} + +export default withRedux(connect(mapStateToProps, mapDispatchToProps)(HeaderBar)) diff --git a/UI/containers/user.js b/UI/containers/user.js new file mode 100644 index 0000000..117f2f7 --- /dev/null +++ b/UI/containers/user.js @@ -0,0 +1,32 @@ +// @flow +import { namespaceConfig } from 'fast-redux' + +export type User = { + id: string, + username: string, + discriminator: string, + avatar: string, + nicknameCache: { + [server: string]: string + } +} + +export type UserState = { + currentUser: User | null, + userCache: { + [id: string]: User + } +} + +const DEFAULT_STATE: UserState = { + currentUser: null, + userCache: {} +} + +export const { + action, getState: getUserStore +} = namespaceConfig('userStore', DEFAULT_STATE) + +export const getCurrentUser = () => async (dispatch: Function) => { + +} diff --git a/UI/pages/_app.js b/UI/pages/_app.js index 8944c01..36cb762 100644 --- a/UI/pages/_app.js +++ b/UI/pages/_app.js @@ -1,6 +1,9 @@ +import * as React from 'react' import App, { Container } from 'next/app' import Head from 'next/head' import GlobalColors from '../components/global-colors' +import SocialCards from '../components/social-cards' +// import RPCClient from '../rpc' class RoleypolyApp extends App { static async getInitialProps ({ Component, ctx }) { @@ -15,6 +18,7 @@ class RoleypolyApp extends App { componentDidMount () { this.loadTypekit(document) + this.waitForFOUC() } loadTypekit (d) { @@ -42,14 +46,26 @@ class RoleypolyApp extends App { s.parentNode.insertBefore(tk, s) } + // wait one second, add FOUC de-protection. + waitForFOUC () { + setTimeout(() => { + document.documentElement.className += ' force-active'// + }, 2000) + } + render () { - const { Component, pageProps, router } = this.props + const { Component, pageProps, router, rpc } = this.props return ( - + + + Roleypoly + + - + + ) } diff --git a/UI/pages/_internal/_server.js b/UI/pages/_internal/_server.js index e1745ab..a54434a 100644 --- a/UI/pages/_internal/_server.js +++ b/UI/pages/_internal/_server.js @@ -1,7 +1,19 @@ -const Server = ({ router: { query: { id } } }) => ( -
- {id} -
-) +// @flow +import * as React from 'react' +import Head from 'next/head' +import type { PageProps } from '../../types' +import SocialCards from '../../components/social-cards' -export default Server +export default class Server extends React.Component { + render () { + return ( +
+ + server name! + + + hello {this.props.router.query.id} +
+ ) + } +} diff --git a/UI/pages/index.js b/UI/pages/index.js index 2bb702b..1d1ef8b 100644 --- a/UI/pages/index.js +++ b/UI/pages/index.js @@ -5,24 +5,24 @@ import Nav from '../components/nav' const Home = () => (
- +