chore(admin): add endpoints for refreshing content
authorxangelo <me@xangelo.ca>
Wed, 4 Dec 2024 16:33:53 +0000 (11:33 -0500)
committerxangelo <me@xangelo.ca>
Wed, 4 Dec 2024 16:33:53 +0000 (11:33 -0500)
The content refresh endpoints are now accessible to admins only via
the Admin panel that is integrated with the "Settings" page.

seeds/monsters.ts
seeds/shop_items.ts
src/server/api.ts
src/server/permissions.ts [new file with mode: 0644]
src/server/routes/admin.ts [new file with mode: 0644]
src/server/routes/index.ts
src/server/views/admin.ts [new file with mode: 0644]
tsconfig.json

index 02eecb821d279c342517f93b7f9ac9e63a20c5db..a8dafe88144fb05e3d4aac517fcc83479e9d4ba3 100644 (file)
@@ -75,9 +75,14 @@ export async function createMonsters(): Promise<void> {
   });
 }
 
+export async function refreshAllMonsterData() {
+  await createFactions();
+  await createMonsters();
+}
+
 // run this script manually
 if(!module.parent) {
-  createFactions().then(createMonsters).then(() => {
+  refreshAllMonsterData().then(() => {
     console.log(stats.monsters);
     console.log('Complete');
     process.exit(0);
index 6fca12aee5f59ce5d329e6ecf4c6824e6d7d4d7e..024c6c523529fcfc91da96ce2bdea1e7f2a002c5 100644 (file)
@@ -82,8 +82,13 @@ export async function createShopItems(): Promise<void> {
 
 }
 
+export async function refreshAllShopItems() {
+  await createShopEquipment();
+  await createShopItems();
+}
+
 if(!module.parent) {
-  createShopEquipment().then(createShopItems).then(() => {
+  refreshAllShopItems().then(() => {
     console.log('Complete');
     process.exit(0);
   }).catch(e => {
index e869b135222402939fbaa3ff6b35352b261b710f..4e0ea3adbbde852dc5fc9fb75ac972c2c18cd4f2 100644 (file)
@@ -35,11 +35,13 @@ import { renderSkills } from './views/skills';
 import { renderMonsterSelector, renderOnlyMonsterSelector } from './views/monster-selector';
 import { renderFight, renderFightPreRound, renderRoundDetails } from './views/fight';
 import { renderTravel, travelButton } from './views/travel';
+import { renderAdminActions } from './views/admin';
 
 // TEMP!
 import { completeDungeonFight, getActiveDungeon, getRoomVists, loadRoom } from './dungeon';
 import { renderDungeon, renderDungeonRoom } from './views/dungeons/room';
 import { flushBuffer, addEvent } from './events';
+import { Permission } from './permissions';
 
 dotenv();
 
@@ -359,6 +361,10 @@ app.get('/settings', authEndpoint, async (req: Request, res: Response) => {
     warning += `<div class="alert error">If you log out without signing up first, this account is lost forever.</div>`;
   }
 
+  if(req.player.permissions.includes(Permission.ADMIN)) {
+    html += `<div class="admin-actions">${renderAdminActions()}</div>`;
+  }
+
   html += '<a href="#" hx-post="/logout">Logout</a>';
   res.send(warning + html);
 });
diff --git a/src/server/permissions.ts b/src/server/permissions.ts
new file mode 100644 (file)
index 0000000..23ae11d
--- /dev/null
@@ -0,0 +1,5 @@
+
+export enum Permission {
+    ADMIN = 'admin',
+    MODERATOR = 'moderator'
+}
diff --git a/src/server/routes/admin.ts b/src/server/routes/admin.ts
new file mode 100644 (file)
index 0000000..0e6ca91
--- /dev/null
@@ -0,0 +1,34 @@
+import { authEndpoint } from '@server/auth';
+import { Permission } from '@server/permissions';
+import { Request, Response, Router } from 'express';
+import { createAllCitiesAndLocations } from '@seeds/cities';
+import { refreshAllShopItems } from '@seeds/shop_items';
+import { refreshAllMonsterData } from '@seeds/monsters';
+import { createDropTables } from '@seeds/drop-tables';
+import { Alert } from '@server/views/alert';
+import { ADMIN_ACTIONS } from '@server/views/admin';
+
+export const adminRouter = Router();
+
+
+adminRouter.post('/admin/content/refresh/:type', authEndpoint, async (req: Request, res: Response) => {
+    if(!req.player.permissions.includes(Permission.ADMIN)) {
+        return res.sendStatus(403);
+    }
+
+    switch(req.params.type) {
+        case 'cities':
+            await createAllCitiesAndLocations();
+            res.send(Alert('success', 'Cities refreshed') + ADMIN_ACTIONS.get('cities')?.label);
+            break;
+        case 'items':
+            await refreshAllShopItems();
+            res.send(Alert('success', 'Shop items refreshed') + ADMIN_ACTIONS.get('items')?.label);
+            break;
+        case 'monsters':
+            await refreshAllMonsterData();
+            await createDropTables();
+            res.send(Alert('success', 'Monsters and Drop tables refreshed') + ADMIN_ACTIONS.get('monsters')?.label);
+            break;
+    }
+});
index 4ee83446cdacd7e2f5b48940de87ff93e9fa84c5..546b2141a0a6e6a048aa0aeee1d1b67d9953cce8 100644 (file)
@@ -6,3 +6,4 @@ export { dungeonRouter } from './locations/dungeon';
 export { healerRouter } from './locations/healer';
 export { recruiterRouter } from './locations/recruiter';
 export { repairRouter } from './locations/repair';
+export { adminRouter } from './admin';
\ No newline at end of file
diff --git a/src/server/views/admin.ts b/src/server/views/admin.ts
new file mode 100644 (file)
index 0000000..d4d0007
--- /dev/null
@@ -0,0 +1,22 @@
+
+export const ADMIN_ACTIONS = new Map<string, { label: string, action: string }>();
+
+ADMIN_ACTIONS.set('cities', {
+    label: 'Refresh Cities',
+    action: 'cities'
+});
+
+
+ADMIN_ACTIONS.set('items', {
+    label: 'Refresh All Items',
+    action: 'items'
+});
+
+ADMIN_ACTIONS.set('monsters', {
+    label: 'Refresh Monsters',
+    action: 'monsters'
+});
+
+export function renderAdminActions() {
+    return Array.from(ADMIN_ACTIONS.values()).map(action => `<button hx-post="/admin/content/refresh/${action.action}">${action.label}</button>`).join('');
+}
index 7d2e2b5363507eb68454bac24952c2974ede1377..0ca48e71b608ef484927dd5a78aea352cd43ae02 100644 (file)
@@ -17,7 +17,8 @@
       "@shared/*": ["src/shared/*"],
       "@server/*": ["src/server/*"],
       "@client/*": ["src/client/*"],
-      "@assets/*": ["public/assets/*"]
+      "@assets/*": ["public/assets/*"],
+      "@seeds/*": ["seeds/*"]
     }
   },
   "include": ["src/server/api.ts"]