From: xangelo Date: Wed, 6 Sep 2023 19:35:15 +0000 (-0400) Subject: chore(release): 0.3.6 X-Git-Tag: v0.3.6 X-Git-Url: https://git.xangelo.ca/?p=risinglegends.git;a=commitdiff_plain;h=v0.3.6;hp=v0.3.5;ds=sidebyside chore(release): 0.3.6 --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 81ae3c7..c9a78dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.3.6](https://git.xangelo.ca/?p=risinglegends.git;a=commitdiff;h=v0.3.6;hp=v0.3.5;ds=sidebyside) (2023-09-06) + + +### Features + +* chat command to set player level 9ced477 +* rbac support with admin permission 940079d + ### [0.3.5](https://git.xangelo.ca/?p=risinglegends.git;a=commitdiff;h=v0.3.5;hp=v0.3.4;ds=sidebyside) (2023-09-06) diff --git a/migrations/20230906180935_rbac.ts b/migrations/20230906180935_rbac.ts new file mode 100644 index 0000000..e110d2d --- /dev/null +++ b/migrations/20230906180935_rbac.ts @@ -0,0 +1,18 @@ +import { Knex } from "knex"; + + +export async function up(knex: Knex): Promise { + return knex.schema.createTable('permissions', function(table){ + table.string('name').primary(); + }).createTable('player_permissions', function(table) { + table.string('permission'); + table.uuid('player_id'); + table.primary(['permission', 'player_id']); + }); +} + + +export async function down(knex: Knex): Promise { + return knex.schema.dropTable('permissions').dropTable('player_permissions'); +} + diff --git a/package-lock.json b/package-lock.json index ac9b526..312fb12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rising-legends", - "version": "0.3.5", + "version": "0.3.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rising-legends", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@honeycombio/opentelemetry-node": "^0.4.0", "@opentelemetry/auto-instrumentations-node": "^0.37.0", diff --git a/package.json b/package.json index 891588d..3c242fc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rising-legends", "private": true, - "version": "0.3.5", + "version": "0.3.6", "scripts": { "up": "npx prisma migrate dev --name \"init\"", "start": "pm2 start dist/server/api.js", diff --git a/src/server/api.ts b/src/server/api.ts index 80aa82a..0aa2689 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -25,6 +25,7 @@ import { clearTravelPlan, completeTravel, getAllPaths, getAllServices, getCityDe import { signup, login, authEndpoint } from './auth'; import {db} from './lib/db'; import { getPlayerSkills} from './skills'; +import { handleChatCommands } from './chat-commands'; import { fightRound, blockPlayerInFight } from './fight'; @@ -45,9 +46,6 @@ import { renderTravel, travelButton } from './views/travel'; import { renderChatMessage } from './views/chat'; // TEMP! -import { createMonsters } from '../../seeds/monsters'; -import { createAllCitiesAndLocations } from '../../seeds/cities'; -import { createShopItems, createShopEquipment } from '../../seeds/shop_items'; import { Item, PlayerItem, ShopItem } from 'shared/items'; import { equip, unequip } from './equipment'; import { HealthPotionSmall } from '../shared/items/health_potion'; @@ -164,27 +162,11 @@ app.post('/chat', authEndpoint, async (req: Request, res: Response) => { return; } - let message: Message; - if(msg.startsWith('/server lmnop')) { + if(msg.startsWith('/server') && req.player.permissions.includes('admin')) { try { - if(msg === '/server lmnop refresh-monsters') { - await createMonsters(); - message = broadcastMessage('server', 'Monster refresh!'); - } - else if(msg === '/server lmnop refresh-cities') { - await createAllCitiesAndLocations(); - message = broadcastMessage('server', 'Cities, Locations, and Paths refreshed!'); - } - else if(msg === '/server lmnop refresh-shops') { - await createShopItems(); - await createShopEquipment(); - message = broadcastMessage('server', 'Refresh shop items'); - } - else { - const str = msg.split('/server lmnop ')[1]; - if(str) { - message = broadcastMessage('server', str); - } + const output = await handleChatCommands(msg, req.player, io); + if(output) { + io.to(cache.get(`socket:${req.player.id}`)).emit('chat', renderChatMessage(output)); } } catch(e) { @@ -199,17 +181,15 @@ app.post('/chat', authEndpoint, async (req: Request, res: Response) => { res.sendStatus(204); } else { - message = broadcastMessage(req.player.username, xss(msg, { + const message = broadcastMessage(req.player.username, xss(msg, { whiteList: {} })); chatHistory.push(message); chatHistory.slice(-10); - } - - if(message) { io.emit('chat', renderChatMessage(message)); - res.sendStatus(204); } + + res.sendStatus(204); }); app.get('/player', authEndpoint, async (req: Request, res: Response) => { diff --git a/src/server/chat-commands.ts b/src/server/chat-commands.ts new file mode 100644 index 0000000..5e43627 --- /dev/null +++ b/src/server/chat-commands.ts @@ -0,0 +1,52 @@ +import { Server } from 'socket.io'; +import { maxHp, maxVigor, Player } from '../shared/player'; +import { createMonsters } from '../../seeds/monsters'; +import { createAllCitiesAndLocations } from '../../seeds/cities'; +import { createShopItems, createShopEquipment } from '../../seeds/shop_items'; +import { broadcastMessage, Message } from '../shared/message'; +import { updatePlayer } from './player'; + +export async function handleChatCommands(msg: string, player: Player, io: Server): Promise { + let message: Message; + if(msg === '/server refresh-monsters') { + await createMonsters(); + message = broadcastMessage('server', 'Monster refresh!'); + } + else if(msg === '/server refresh-cities') { + await createAllCitiesAndLocations(); + message = broadcastMessage('server', 'Cities, Locations, and Paths refreshed!'); + } + else if(msg === '/server refresh-shops') { + await createShopItems(); + await createShopEquipment(); + message = broadcastMessage('server', 'Refresh shop items'); + } + else if(msg.startsWith('/server set-level')) { + const level = parseInt(msg.split(' ').pop()); + if(level < 1) { + message = broadcastMessage('server', 'Needs to be at least level 1'); + } + else { + message = broadcastMessage('server', `Set player level: ${level}`); + + player.level = level; + player.strength = 4; + player.constitution = 4; + player.dexterity = 4; + player.intelligence = 4; + player.hp = maxHp(player.constitution, player.level); + player.vigor = maxVigor(player.constitution, player.level); + player.stat_points = level-1; + + await updatePlayer(player); + } + } + else { + const str = msg.split('/server ')[1]; + if(str) { + message = broadcastMessage('server', str); + } + } + + return message; +} diff --git a/src/server/player.ts b/src/server/player.ts index 527069b..a9d077c 100644 --- a/src/server/player.ts +++ b/src/server/player.ts @@ -7,7 +7,12 @@ import {logger} from './lib/logger'; export async function loadPlayer(authToken: string): Promise { const res = await db.first() - .select('players.*', 'profession_levels.level', 'profession_levels.exp') + .select( + 'players.*', + 'profession_levels.level', + 'profession_levels.exp', + db.raw(`coalesce(pp.permissions, '[]'::json) as permissions`) + ) .from('players') .join('profession_levels', function() { this.on(function() { @@ -15,11 +20,15 @@ export async function loadPlayer(authToken: string): Promise { this.andOn('profession_levels.profession', '=', 'players.profession') }) }) + .leftJoin( + db.raw(`(select json_agg(pp.permission) as permissions, pp.player_id from player_permissions pp group by pp.player_id) pp`), + 'pp.player_id','=', 'players.id' + ) .where({ 'players.id': authToken }); - return res; + return res; } export async function createPlayer(): Promise { diff --git a/src/shared/player.ts b/src/shared/player.ts index b7c172a..93d75f3 100644 --- a/src/shared/player.ts +++ b/src/shared/player.ts @@ -3,6 +3,8 @@ import { Stat } from './stats'; import { SkillDefinition, Skill } from './skills'; import { EquippedItemDetails } from './equipped'; +export type Permission = 'admin' | 'moderator'; + export type Player = { id: string, account_type: 'session' | 'auth', @@ -21,6 +23,7 @@ export type Player = { city_id: number; stat_points: number; vigor: number; + permissions: Permission[] } export type PlayerWithSkills = Player & {