.env
node_modules/
+dist/
"name": "control-up-takehome",
"scripts": {
"up": "npx prisma migrate dev --name \"init\"",
- "start": "npx ts-node src/api.ts",
+ "start": "pm2 start dist/server/api.js",
+ "build": "npx webpack && npx tsc",
"dev:client": "npx webpack -w",
"dev:server": "npx nodemon src/server/api.ts"
},
import { join } from 'path';
import { readFile } from 'fs';
import { promisify } from 'util';
-import {Monster} from "@shared/monsters";
+import {Monster} from "../src/shared/monsters";
const read = promisify(readFile);
-import {ShopItem} from "@shared/inventory";
+import {ShopItem} from "../src/shared/inventory";
import { Knex } from "knex";
export async function seed(knex: Knex): Promise<void> {
// Deletes ALL existing entries
await knex("shop_items").del();
+ await knex("equipped").del();
+ await knex("inventory").del();
const data: Partial<ShopItem>[] = [
{
-import {Message} from '@shared/message';
+import {Message} from '../shared/message';
import { io } from 'socket.io-client';
import $ from 'jquery';
-import {expToLevel, maxHp, Player} from '@shared/player';
+import {expToLevel, maxHp, Player} from '../shared/player';
import { authToken, http } from './http';
import { CustomEventManager } from './events';
-import {MonsterForFight, MonsterForList} from '@shared/monsters';
-import {FightRound} from '@shared/fight';
-import { city } from '@shared/map'
+import {MonsterForFight, MonsterForList} from '../shared/monsters';
+import {FightRound} from '../shared/fight';
+import { city } from '../shared/map'
import { v4 as uuid } from 'uuid';
-import {InventoryItem, ShopItem, SubType} from '@shared/inventory';
-import {EquippedItemDetails} from '@shared/equipped';
+import {ShopItem, SubType} from '../shared/inventory';
+import {EquippedItemDetails} from '../shared/equipped';
const cache = new Map<string, any>();
const events = new CustomEventManager();
import express, {Request, Response} from 'express';
import http from 'http';
import { Server, Socket } from 'socket.io';
-import { logger } from '@lib/logger';
+import { logger } from './lib/logger';
import { loadPlayer, createPlayer, updatePlayer } from './player';
-import { redis } from '@lib/redis';
import * as _ from 'lodash';
-import {broadcastMessage} from '@shared/message';
-import {expToLevel, maxHp, Player, professionList} from '@shared/player';
+import {broadcastMessage} from '../shared/message';
+import {expToLevel, maxHp, Player, professionList} from '../shared/player';
import {clearFight, createFight, getMonsterList, loadMonster, loadMonsterFromFight, saveFightState} from './monster';
-import {FightRound} from '@shared/fight';
+import {FightRound} from '../shared/fight';
import {addInventoryItem, getEquippedItems, getInventory, getInventoryItem, updateAp} from './inventory';
-import {Monster, MonsterForList} from '@shared/monsters';
+import {Monster, MonsterForList} from '../shared/monsters';
import {getShopItem, listShopItems} from './shopItem';
import {equip, unequip} from './equipment';
import { v4 as uuid } from 'uuid';
-import {calculateMaxAp, EquippedItemDetails} from '@shared/equipped';
-import {ArmourType, SubType} from '@shared/inventory';
+import {EquippedItemDetails} from '../shared/equipped';
+import {ArmourType, SubType} from '../shared/inventory';
dotenv();
const io = new Server(server);
+const cache: Record<string, any> = {};
+
function calcAp(inventoryItem: EquippedItemDetails[], socket: Socket) {
const ap: Record<SubType, {currentAp: number, maxAp: number}> | Record<any, any> = {};
inventoryItem.forEach(item => {
player = await createPlayer();
}
- await redis.set(`token:${player.id}`, socket.id);
+ cache[`token:${player.id}`] = socket.id;
logger.log(`Socket [${socket.id}] auth token: ${player.id}`);
-import {db} from "@lib/db";
-import {EquippedItem, EquippedItemDetails} from "@shared/equipped";
-import {InventoryItem} from "@shared/inventory";
+import {db} from "./lib/db";
+import {EquippedItem, EquippedItemDetails} from "../shared/equipped";
+import {InventoryItem} from "../shared/inventory";
export async function getEquippedItems(playerId: string): Promise<EquippedItem[]> {
return db.raw<EquippedItemDetails[]>(`
-import {InventoryItem, ShopItem} from "@shared/inventory";
-import { db} from '@lib/db';
-import {EquippedItemDetails} from "@shared/equipped";
+import {InventoryItem, ShopItem} from "../shared/inventory";
+import { db} from './lib/db';
+import {EquippedItemDetails} from "../shared/equipped";
export async function addInventoryItem(playerId: string, item: ShopItem) {
+++ /dev/null
-import { Redis } from 'ioredis';
-
-export const redis = new Redis();
+++ /dev/null
-import { Router } from 'express';
-import { TObject } from '@sinclair/typebox';
-import { TypeCompiler } from '@sinclair/typebox/compiler';
-import type { Request, Response } from 'express';
-
-type Schema = {
- input?: TObject<any>,
- output?: TObject<any>
-};
-
-export type HttpHandler<I, O> = (params: I, rawReq: Request, rawRes: Response) => Promise<O>;
-
-class InvalidUserInputError extends Error {
- statusCode: number;
- constructor(msg: string) {
- super(msg);
- this.statusCode = 400;
- }
-}
-
-export class HttpRouter {
- router: Router;
- prefix: string;
- constructor(prefix: string = '') {
- this.router = Router();
- this.prefix = prefix;
- }
-
- wrap<I, O>(schema: Schema, handler: HttpHandler<I, O>) {
- return async function (req: Request, res: Response) {
- try {
- const start = Date.now();
- console.info(`Req: ${req.method.toUpperCase()} ${req.originalUrl}`);
-
- const rawParams = {
- body: req.body,
- params: req.params,
- query: req.query,
- headers: req.headers
- };
-
- if(schema.input) {
- const validator = TypeCompiler.Compile(schema.input);
- const validatorOutput = validator.Check(rawParams);
- if(!validatorOutput) {
- const errors = [...validator.Errors(rawParams)];
- console.error('Validation Errors', errors);
- throw new InvalidUserInputError('Invalid user input');
- }
- }
-
- const output = await handler(rawParams as I, req, res);
- console.info(`Runtime: ${Date.now() - start}ms`);
-
- if(output === undefined) {
- res.statusCode = 204;
- }
- else if(typeof output === 'string') {
- res.send(output);
- }
- else {
- res.json(output);
- }
- }
- catch(e) {
- console.error(e);
- res.status(e.statusCode || 500).send({
- error: e.message
- });
- }
- finally {
- res.end();
- }
- }
- }
-
- get<I, O>(schema: Schema, endpoint: string, handler: HttpHandler<I, O>): void {
- console.debug(`Mapped GET ${this.prefix+endpoint}`);
- this.router.get(endpoint, this.wrap<I, O>(schema, handler));
- }
-
- post<I, O>(schema: Schema, endpoint: string, handler: HttpHandler<I, O>): void {
- console.debug(`Mapped POST ${this.prefix+endpoint}`);
- this.router.post(endpoint, this.wrap<I, O>(schema, handler));
- }
-
- delete<I, O>(schema: Schema, endpoint: string, handler: HttpHandler<I, O>): void {
- console.debug(`Mapped DELETE ${this.prefix+endpoint}`);
- this.router.post(endpoint, this.wrap<I, O>(schema, handler));
- }
-}
+++ /dev/null
-import express from 'express';
-import bodyParser from 'body-parser';
-import { HttpRouter } from './router';
-
-export class HttpServer {
- server: express.Application;
- port: number;
- constructor(port: string | number = '9090') {
- this.port = parseInt(port.toString(), 10);
- this.server = express();
- this.configureMiddleWare();
- }
-
- configureMiddleWare() {
- this.server.use(express.json());
- this.server.use(bodyParser.json());
- this.server.use(bodyParser.urlencoded({ extended: true }));
- }
-
- useRouter(router: HttpRouter) {
- this.server.use(router.prefix, router.router);
- }
-
- start(fn?: any): void {
- console.info(`Listening on port ${this.port}`);
- this.server.listen(this.port, fn?.bind(this));
- }
-}
-
-export let server: HttpServer;
-
-export function createServer(port?: string | number): HttpServer {
- if(!server) {
- server = new HttpServer(port);
- }
-
- return server;
-}
-import { db } from '@lib/db';
-import { Fight, Monster } from '@shared/monsters';
+import { db } from './lib/db';
+import { Fight, Monster } from '../shared/monsters';
export async function getMonsterList(): Promise<Monster[]> {
const res: Monster[] = await db.select('*')
-import { db } from '@lib/db';
-import {Player, maxHp} from "@shared/player";
+import { db } from './lib/db';
+import {Player, maxHp} from "../shared/player";
import { random } from 'lodash';
export async function loadPlayer(authToken: string): Promise<Player> {
-import { db } from '@lib/db';
-import {ShopItem} from '@shared/inventory';
+import { db } from './lib/db';
+import {ShopItem} from '../shared/inventory';
export function listShopItems(where: Partial<ShopItem>): Promise<ShopItem[]> {
return db.select('*').from<ShopItem>('shop_items')
-import {InventoryItem, InventoryType, SubType} from "@shared/inventory";
+import {InventoryItem, InventoryType, SubType} from "./inventory";
export type EquippedItem = {
item_id: string;
-import * as _ from 'lodash';
-
export type Player = {
id: string,
profession: Profession,
"preserveConstEnums": true,
"sourceMap": true,
"baseUrl": "src",
- "paths": {},
+ "outDir": "dist",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
- "resolveJsonModule": true,
- "paths": {
- "@lib/*": ["server/lib/*"],
- "@shared/*": ["shared/*"],
- }
+ "resolveJsonModule": true
},
- "ts-node": {
- "require": ["tsconfig-paths/register"]
- }
+ "include": ["src/server/api.ts"]
}