feat: migrate to augmenting express.Request interface
authorxangelo <me@xangelo.ca>
Tue, 5 Sep 2023 15:39:00 +0000 (11:39 -0400)
committerxangelo <me@xangelo.ca>
Tue, 5 Sep 2023 15:39:00 +0000 (11:39 -0400)
We were originally extending the express.Request interface inline in
`src/server/auth.ts`, We are now just augmenting the actual
express.Request interface via typescript definition.

src/server/api.ts
src/server/auth.ts
src/server/fight.ts
src/server/locations/healer.ts
src/server/locations/recruiter.ts
src/server/locations/repair.ts
src/types/express/index.d.ts [new file with mode: 0644]
tsconfig.json

index f167ff61bf0ba9cf5a3b1bb154485e11f29ad784..355bd04a72f60ca9639f2d84217a97b0d09974b9 100644 (file)
@@ -22,7 +22,7 @@ import {FightTrigger, Monster, MonsterForFight} from '../shared/monsters';
 import {getShopEquipment, listShopItems } from './shopEquipment';
 import {EquipmentSlot} from '../shared/inventory';
 import { clearTravelPlan, completeTravel, getAllPaths, getAllServices, getCityDetails, getService, getTravelPlan, stepForward, travel } from './map';
-import { signup, login, authEndpoint, AuthRequest } from './auth';
+import { signup, login, authEndpoint } from './auth';
 import {db} from './lib/db';
 import { getPlayerSkills} from './skills';
 
@@ -150,13 +150,13 @@ app.use(professionRouter);
 app.use(repairRouter);
 
 
-app.get('/chat/history', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/chat/history', authEndpoint, async (req: Request, res: Response) => {
   let html = chatHistory.map(renderChatMessage);
 
   res.send(html.join("\n"));
 });
 
-app.post('/chat', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/chat', authEndpoint, async (req: Request, res: Response) => {
   const msg = req.body.message.trim();
 
   if(!msg || !msg.length) {
@@ -207,12 +207,12 @@ app.post('/chat', authEndpoint, async (req: AuthRequest, res: Response) => {
   }
 });
 
-app.get('/player', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/player', authEndpoint, async (req: Request, res: Response) => {
   const equipment = await getEquippedItems(req.player.id);
   res.send(renderPlayerBar(req.player) + renderProfilePage(req.player, equipment));
 });
 
-app.post('/player/stat/:stat', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/player/stat/:stat', authEndpoint, async (req: Request, res: Response) => {
   const equipment = await getEquippedItems(req.player.id);
   const stat = req.params.stat;
   if(!['strength', 'constitution', 'dexterity', 'intelligence'].includes(stat)) {
@@ -235,13 +235,13 @@ app.post('/player/stat/:stat', authEndpoint, async (req: AuthRequest, res: Respo
   res.send(renderPlayerBar(req.player) + renderProfilePage(req.player, equipment));
 });
 
-app.get('/player/skills', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/player/skills', authEndpoint, async (req: Request, res: Response) => {
   const skills = await getPlayerSkills(req.player.id);
 
   res.send(renderSkills(skills));
 });
 
-app.get('/player/inventory', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/player/inventory', authEndpoint, async (req: Request, res: Response) => {
   const [inventory, items] = await Promise.all([
     getInventory(req.player.id),
     getPlayersItems(req.player.id)
@@ -250,7 +250,7 @@ app.get('/player/inventory', authEndpoint, async (req: AuthRequest, res: Respons
   res.send(renderInventoryPage(inventory, items));
 });
 
-app.post('/player/equip/:item_id/:slot', authEndpoint, blockPlayerInFight, async (req: AuthRequest, res: Response) => {
+app.post('/player/equip/:item_id/:slot', authEndpoint, blockPlayerInFight, async (req: Request, res: Response) => {
   const inventoryItem = await getInventoryItem(req.player.id, req.params.item_id);
   const equippedItems = await getEquippedItems(req.player.id);
   const requestedSlot = req.params.slot;
@@ -302,7 +302,7 @@ app.post('/player/equip/:item_id/:slot', authEndpoint, blockPlayerInFight, async
   res.send(renderInventoryPage(inventory, items, inventoryItem.type) + renderPlayerBar(req.player));
 });
 
-app.post('/player/unequip/:item_id', authEndpoint, blockPlayerInFight, async (req: AuthRequest, res: Response) => {
+app.post('/player/unequip/:item_id', authEndpoint, blockPlayerInFight, async (req: Request, res: Response) => {
   const [item, ] = await Promise.all([
     getInventoryItem(req.player.id, req.params.item_id),
     unequip(req.player.id, req.params.item_id)
@@ -316,7 +316,7 @@ app.post('/player/unequip/:item_id', authEndpoint, blockPlayerInFight, async (re
   res.send(renderInventoryPage(inventory, items, item.type) + renderPlayerBar(req.player));
 });
 
-app.get('/player/explore', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/player/explore', authEndpoint, async (req: Request, res: Response) => {
   const fight = await loadMonsterFromFight(req.player.id);
   const travelPlan = await getTravelPlan(req.player.id);
   let closestTown = req.player.city_id;
@@ -375,7 +375,7 @@ app.get('/player/explore', authEndpoint, async (req: AuthRequest, res: Response)
 });
 
 // used to purchase equipment from a particular shop
-app.put('/location/:location_id/equipment/:item_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.put('/location/:location_id/equipment/:item_id', authEndpoint, async (req: Request, res: Response) => {
   const item = await getShopEquipment(parseInt(req.params.item_id), parseInt(req.params.location_id));
 
   if(!item) {
@@ -397,7 +397,7 @@ app.put('/location/:location_id/equipment/:item_id', authEndpoint, async (req: A
 });
 
 // used to purchase items from a particular shop
-app.put('/location/:location_id/items/:item_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.put('/location/:location_id/items/:item_id', authEndpoint, async (req: Request, res: Response) => {
   const item: (ShopItem & Item) = await getItemFromShop(parseInt(req.params.item_id), parseInt(req.params.location_id));
 
   if(!item) {
@@ -421,7 +421,7 @@ app.put('/location/:location_id/items/:item_id', authEndpoint, async (req: AuthR
 // used to display equipment modals in a store, validates that 
 // the equipment is actually in this store before displaying 
 // the modal
-app.get('/location/:location_id/equipment/:item_id/overview', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/location/:location_id/equipment/:item_id/overview', authEndpoint, async (req: Request, res: Response) => {
   const equipment = await getShopEquipment(parseInt(req.params.item_id), parseInt(req.params.location_id));
 
   if(!equipment) {
@@ -452,7 +452,7 @@ app.get('/location/:location_id/equipment/:item_id/overview', authEndpoint, asyn
 // used to display item modals in a store, validates that 
 // the item is actually in this store before displaying 
 // the modal
-app.get('/location/:location_id/items/:item_id/overview', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/location/:location_id/items/:item_id/overview', authEndpoint, async (req: Request, res: Response) => {
   const item: (ShopItem & Item) = await getItemFromShop(parseInt(req.params.item_id), parseInt(req.params.location_id));
 
   if(!item) {
@@ -481,7 +481,7 @@ app.get('/location/:location_id/items/:item_id/overview', authEndpoint, async (r
   res.send(html);
 });
 
-app.put('/item/:item_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.put('/item/:item_id', authEndpoint, async (req: Request, res: Response) => {
   const item: PlayerItem = await getItemFromPlayer(req.player.id, parseInt(req.params.item_id));
 
   if(!item) {
@@ -524,7 +524,7 @@ app.put('/item/:item_id', authEndpoint, async (req: AuthRequest, res: Response)
 
 });
 
-app.get('/modal/items/:item_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/modal/items/:item_id', authEndpoint, async (req: Request, res: Response) => {
   const item: PlayerItem = await getItemFromPlayer(req.player.id, parseInt(req.params.item_id));
 
   if(!item) {
@@ -553,7 +553,7 @@ app.get('/modal/items/:item_id', authEndpoint, async (req: AuthRequest, res: Res
   res.send(html);
 });
 
-app.get('/city/stores/city:stores/:location_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/city/stores/city:stores/:location_id', authEndpoint, async (req: Request, res: Response) => {
   const location = await getService(parseInt(req.params.location_id));
 
   if(!location || location.city_id !== req.player.city_id) {
@@ -570,7 +570,7 @@ app.get('/city/stores/city:stores/:location_id', authEndpoint, async (req: AuthR
   res.send(html);
 });
 
-app.get('/city/explore/city:explore/:location_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/city/explore/city:explore/:location_id', authEndpoint, async (req: Request, res: Response) => {
   const location = await getService(parseInt(req.params.location_id));
   if(!location || location.city_id !== req.player.city_id) {
 
@@ -582,7 +582,7 @@ app.get('/city/explore/city:explore/:location_id', authEndpoint, async (req: Aut
   res.send(renderOnlyMonsterSelector(monsters, 0, location));
 });
 
-app.post('/travel', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/travel', authEndpoint, async (req: Request, res: Response) => {
   const destination_id = parseInt(req.body.destination_id);
 
   if(!destination_id || isNaN(destination_id)) {
@@ -595,7 +595,7 @@ app.post('/travel', authEndpoint, async (req: AuthRequest, res: Response) => {
   res.json(travelPlan);
 });
 
-app.post('/fight/turn', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/fight/turn', authEndpoint, async (req: Request, res: Response) => {
   const fightBlockKey = `fightturn:${req.player.id}`;
 
   if(cache[fightBlockKey] && cache[fightBlockKey] > Date.now()) {
@@ -646,7 +646,7 @@ app.post('/fight/turn', authEndpoint, async (req: AuthRequest, res: Response) =>
   res.send(html + travelSection + playerBar);
 });
 
-app.post('/fight', fightRateLimiter, authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/fight', fightRateLimiter, authEndpoint, async (req: Request, res: Response) => {
   if(req.player.hp <= 0) {
     logger.log(`Player didn\'t have enough hp`);
     return res.sendStatus(400);
@@ -688,7 +688,7 @@ app.post('/fight', fightRateLimiter, authEndpoint, async (req: AuthRequest, res:
   res.send(renderFightPreRound(data, true, location, location.city_id));
 });
 
-app.post('/travel/step', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/travel/step', authEndpoint, async (req: Request, res: Response) => {
   const stepTimerKey = `step:${req.player.id}`;
 
   const travelPlan = await getTravelPlan(req.player.id);
@@ -755,7 +755,7 @@ app.post('/travel/step', authEndpoint, async (req: AuthRequest, res: Response) =
   }
 });
 
-app.post('/travel/return-to-source', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/travel/return-to-source', authEndpoint, async (req: Request, res: Response) => {
   // puts the player back in their starting town
   // doesn't matter if they don't have one
   // redirect them!
@@ -789,7 +789,7 @@ app.post('/travel/return-to-source', authEndpoint, async (req: AuthRequest, res:
 
 });
 
-app.post('/travel/:destination_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/travel/:destination_id', authEndpoint, async (req: Request, res: Response) => {
 if(req.player.hp <= 0) {
     logger.log(`Player didn\'t have enough hp`);
     res.send(Alert.ErrorAlert('Sorry, you need some HP to start travelling.'));
@@ -814,7 +814,7 @@ if(req.player.hp <= 0) {
   }));
 });
 
-app.get('/settings', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.get('/settings', authEndpoint, async (req: Request, res: Response) => {
   let warning = '';
   let html = '';
   if(req.player.account_type === 'session') {
@@ -825,7 +825,7 @@ app.get('/settings', authEndpoint, async (req: AuthRequest, res: Response) => {
   res.send(warning + html);
 });
 
-app.post('/logout', authEndpoint, async (req: AuthRequest, res: Response) => {
+app.post('/logout', authEndpoint, async (req: Request, res: Response) => {
   // ref to get the socket id for a particular player
   cache.delete(`socket:${req.player.id}`);
   // ref to get the player object
index 456d2937a7f755c174411f3c5d709061705a0f9b..9b4a3a41d38ddfbc2160e881e7cf94f78039b829 100644 (file)
@@ -6,10 +6,6 @@ import { Auth } from '../shared/auth';
 import { db } from './lib/db';
 import { Request, Response } from 'express';
 
-export interface AuthRequest extends Request {
-  player: Player
-}
-
 export async function signup(playerId: string, username: string, password: string): Promise<void> {
   const salt = await bcrypt.genSalt(10);
   const hash = await bcrypt.hash(password, salt);
@@ -63,7 +59,7 @@ export async function login(username: string, password: string): Promise<Player>
 
 }
 
-export async function authEndpoint(req: AuthRequest, res: Response, next: any) {
+export async function authEndpoint(req: Request, res: Response, next: any) {
   const authToken = req.headers['x-authtoken'];
   if(!authToken) {
     console.log(`Invalid auth token ${authToken}`);
index 6e262dfe1c2ea34329de281d90543686715dc7f4..c1bdc453d37d1082422252459201cdac2ca12fce 100644 (file)
@@ -9,11 +9,10 @@ import { EquipmentSlot } from '../shared/inventory';
 import { MonsterWithFaction, MonsterForFight } from '../shared/monsters';
 import { getPlayerSkillsAsObject, updatePlayerSkills } from './skills';
 import { SkillID, Skills } from '../shared/skills';
-import { AuthRequest } from './auth';
-import { Response } from 'express';
+import { Request, Response } from 'express';
 import * as Alert from './views/alert';
 
-export async function blockPlayerInFight(req: AuthRequest, res: Response, next: any) {
+export async function blockPlayerInFight(req: Request, res: Response, next: any) {
   const fight = await loadMonsterFromFight(req.player.id);
   if(!fight) {
     next();
index 2d12db0e5b7a093e7e3cfdcd6ade83cd8f0a86b4..3d7374b0d57af405368e851e3a04b9edc57ad628 100644 (file)
@@ -1,6 +1,6 @@
-import { Response, Router } from "express";
+import { Request, Response, Router } from "express";
 import { maxHp, maxVigor } from "../../shared/player";
-import { authEndpoint, AuthRequest } from '../auth';
+import { authEndpoint } from '../auth';
 import { logger } from "../lib/logger";
 import { updatePlayer } from "../player";
 import { getCityDetails, getService } from '../map';
@@ -91,7 +91,7 @@ function getText(type: TextSegment, location: Location, city: City): string {
 
 }
 
-router.get('/city/services/healer/:location_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+router.get('/city/services/healer/:location_id', authEndpoint, async (req: Request, res: Response) => {
   const service = await getService(parseInt(req.params.location_id));
   const city = await getCityDetails(service.city_id);
 
@@ -134,7 +134,7 @@ ${BackToTown()}
 
 
 
-router.post('/city/services/healer/heal/:location_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+router.post('/city/services/healer/heal/:location_id', authEndpoint, async (req: Request, res: Response) => {
   const service = await getService(parseInt(req.params.location_id));
   const city = await getCityDetails(service.city_id);
 
index 96f720d656cf6ce4332acc050ca956faef0d68cc..fa4686089b1c672b360f53728868dfe6444409cb 100644 (file)
@@ -1,6 +1,6 @@
-import { Response, Router } from "express";
+import { Request, Response, Router } from "express";
 import { getService } from "../map";
-import { authEndpoint, AuthRequest } from '../auth';
+import { authEndpoint } from '../auth';
 import { logger } from "../lib/logger";
 import * as Alert from "../views/alert";
 import { changeProfession } from "../player";
@@ -15,7 +15,7 @@ export const router = Router();
 
 const MIN_LEVEL = 25;
 
-router.get('/city/services/profession_recruitor/:location_id', authEndpoint, async(req: AuthRequest, res: Response) => {
+router.get('/city/services/profession_recruitor/:location_id', authEndpoint, async(req: Request, res: Response) => {
   const service = await getService(parseInt(req.params.location_id));
 
   if(!service || service.city_id !== req.player.city_id) {
@@ -80,7 +80,7 @@ router.get('/city/services/profession_recruitor/:location_id', authEndpoint, asy
   `);
 });
 
-router.post('/city/services/profession_change/:location_id', authEndpoint, async(req: AuthRequest, res: Response) => {
+router.post('/city/services/profession_change/:location_id', authEndpoint, async(req: Request, res: Response) => {
   const service = await getService(parseInt(req.params.location_id));
 
   if(!service || service.city_id !== req.player.city_id) {
index 6bb742333de4033847f0d2860805201a635aa53c..19d382c3d17cab0e187f074a9cc492df182364d1 100644 (file)
@@ -1,5 +1,5 @@
-import { Response, Router } from "express";
-import { authEndpoint, AuthRequest } from '../auth';
+import { Request, Response, Router } from "express";
+import { authEndpoint } from '../auth';
 import { logger } from "../lib/logger";
 import { getService } from "../map";
 import { getInventory, getInventoryItem, repair } from '../inventory';
@@ -11,7 +11,7 @@ import { renderPlayerBar } from "../views/player-bar";
 
 export const router = Router();
 
-router.get('/city/services/repair/:location_id', authEndpoint, async(req: AuthRequest, res: Response) => {
+router.get('/city/services/repair/:location_id', authEndpoint, async(req: Request, res: Response) => {
   const service = await getService(parseInt(req.params.location_id));
 
   if(!service || service.city_id !== req.player.city_id) {
@@ -28,7 +28,7 @@ router.get('/city/services/repair/:location_id', authEndpoint, async(req: AuthRe
   res.send(renderRepairService(damaged, req.player, service));
 });
 
-router.post('/city/services/:location_id/repair/:item_id', authEndpoint, async (req: AuthRequest, res: Response) => {
+router.post('/city/services/:location_id/repair/:item_id', authEndpoint, async (req: Request, res: Response) => {
   const service = await getService(parseInt(req.params.location_id));
 
   if(!service || service.city_id !== req.player.city_id) {
diff --git a/src/types/express/index.d.ts b/src/types/express/index.d.ts
new file mode 100644 (file)
index 0000000..9f12cde
--- /dev/null
@@ -0,0 +1,7 @@
+import { Player } from '../../shared/player';
+
+declare module 'express-serve-static-core' {
+  interface Request {
+    player: Player
+  }
+}
index 47b69bd289823e2a3d2545dce673f133e0622532..fa4cf87e3095b149436632e588feb6e01935365e 100644 (file)
@@ -1,17 +1,18 @@
 {
-    "compilerOptions": {
-        "module": "commonjs",
-        "esModuleInterop": true,
-        "target": "es6",
-        "moduleResolution": "node",
-        "removeComments": true,
-        "preserveConstEnums": true,
-        "sourceMap": true,
-        "baseUrl": "src",
-        "outDir": "dist",
-        "emitDecoratorMetadata": true,
-        "experimentalDecorators": true,
-        "resolveJsonModule": true
-    },
-    "include": ["src/server/api.ts"]
+  "compilerOptions": {
+    "module": "commonjs",
+    "esModuleInterop": true,
+    "target": "es6",
+    "moduleResolution": "node",
+    "removeComments": true,
+    "preserveConstEnums": true,
+    "sourceMap": true,
+    "baseUrl": "src",
+    "outDir": "dist",
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "resolveJsonModule": true,
+    "typeRoots": ["./src/types"],
+  },
+  "include": ["src/server/api.ts"]
 }