From a64195bbeda7c95f25b8cc6797032dbad5716eab Mon Sep 17 00:00:00 2001 From: ScrawnyRonnie Date: Fri, 20 Dec 2024 19:15:48 -0500 Subject: [PATCH] For PR 1247 roles --- api/admin.js | 60 ++++++++++++++++++++ api/db.js | 58 +++++++++++++++++-- app/components/AccountLink.svelte | 2 +- app/components/ActionButtons.svelte | 4 +- app/components/CharacterLink.svelte | 6 ++ app/components/PermissionButtons.svelte | 38 +++++++++++++ app/components/RoleModal.svelte | 74 +++++++++++++++++++++++++ app/views/AdminPanel.svelte | 7 +++ app/views/CharacterList.svelte | 5 +- app/views/Profile.svelte | 5 +- app/views/RoleList.svelte | 52 +++++++++++++++++ app/views/UserList.svelte | 2 +- 12 files changed, 302 insertions(+), 11 deletions(-) create mode 100644 app/components/PermissionButtons.svelte create mode 100644 app/components/RoleModal.svelte create mode 100644 app/views/RoleList.svelte diff --git a/api/admin.js b/api/admin.js index 5b49b52..fe4c5e7 100644 --- a/api/admin.js +++ b/api/admin.js @@ -127,4 +127,64 @@ api.get('/characters', NEED_ADMIN, async (req, res, next) => { } }); +api.get('/roles', NEED_ADMIN, async (req, res, next) => { + const pagination = get_pagination(req); + + try { + const roles = await db.get_roles(pagination, db.CHARACTER.LAST_LOGIN, db.SQL_ORDER.DESCENDING); + res.status(200).json({ characters: roles, page: pagination}) + } catch (e) { + console.log(e) + res.status(500).json({ message: 'error' }); + } +}); + +api.post('/roles/:avatar/add_gm', NEED_ADMIN, async (req, res, next) => { + const avatar = parseInt(req.params.avatar); + + try { + await db.update_roles(avatar, {[db.AVATARMODEPERMISSION.GM] : true}) + res.status(200).json({}); + } catch(e) { + console.log(e); + res.status(500).json({ message: 'error' }); + } +}); + +api.post('/roles/:avatar/remove_gm', NEED_ADMIN, async (req, res, next) => { + const avatar = parseInt(req.params.avatar); + + try { + await db.update_roles(avatar, {[db.AVATARMODEPERMISSION.GM] : false}) + res.status(200).json({}); + } catch(e) { + console.log(e); + res.status(500).json({ message: 'error' }); + } +}); + +api.post('/roles/:avatar/add_spectate', NEED_ADMIN, async (req, res, next) => { + const avatar = parseInt(req.params.avatar); + + try { + await db.update_roles(avatar, {[db.AVATARMODEPERMISSION.SPECTATE] : true}) + res.status(200).json({}); + } catch(e) { + console.log(e); + res.status(500).json({ message: 'error' }); + } +}); + +api.post('/roles/:avatar/remove_spectate', NEED_ADMIN, async (req, res, next) => { + const avatar = parseInt(req.params.avatar); + + try { + await db.update_roles(avatar, {[db.AVATARMODEPERMISSION.SPECTATE] : false}) + res.status(200).json({}); + } catch(e) { + console.log(e); + res.status(500).json({ message: 'error' }); + } +}); + export default api; diff --git a/api/db.js b/api/db.js index 38a789f..813a5a4 100644 --- a/api/db.js +++ b/api/db.js @@ -72,6 +72,13 @@ export const AVATAR = Object.freeze({ HEAD: Symbol("head_id"), }); +export const AVATARMODEPERMISSION = Object.freeze({ + THIS: Symbol("avatarmodepermission"), + ID: Symbol("avatar_id"), + GM: Symbol("can_gm"), + SPECTATE: Symbol("can_spectate") +}); + export const WEAPONSTAT = Object.freeze({ THIS: Symbol("weapon"), ID: Symbol("avatar_id"), @@ -314,7 +321,7 @@ export async function get_characters(pagination, sort, order) { try { const char_count = await get_row_count(CHARACTER.THIS); - const chars = await pool.query(`SELECT id, account_id, name, faction_id, created, last_login FROM avatar ORDER BY ${to_sql(sort)} ${to_sql(order)} OFFSET $1 LIMIT $2`, values); + const chars = await pool.query(`SELECT id, account_id, name, faction_id, created, last_login, avatar_id, can_gm, can_spectate FROM avatar LEFT JOIN avatarmodepermission ON avatar_id = id ORDER BY ${to_sql(sort)} ${to_sql(order)} OFFSET $1 LIMIT $2`, values); pagination.item_count = char_count; pagination.page_count = Math.ceil(pagination.item_count / pagination.items_per_page); @@ -327,6 +334,24 @@ export async function get_characters(pagination, sort, order) { } } +export async function get_roles(pagination) { + const start_id = (pagination.page - 1) * pagination.items_per_page; + const values = [start_id, pagination.items_per_page]; + + try { + const roles = await pool.query(`SELECT avatar_id, can_spectate, can_gm, id, last_login, account_id, name FROM avatarmodepermission INNER JOIN avatar ON avatar_id = id WHERE can_gm = TRUE OR can_spectate = TRUE ORDER BY last_login DESC OFFSET $1 LIMIT $2`, values); + const char_count = roles.rowCount; + pagination.item_count = char_count; + pagination.page_count = Math.ceil(pagination.item_count / pagination.items_per_page); + + return roles.rows; + } catch (e) { + if (e.code) + e.code = pg_error_inv[e.code] + throw e; + } +} + // Database action added for the sake of reporting avatar information out to a publicly exposed API route for leader-boards and other components. export async function get_character_batch_for_stats(batch, sort, order) { const values = [batch]; @@ -415,7 +440,7 @@ export async function get_top_kills_byDate() { export async function get_characters_by_account(account_id) { try { - const characters = await pool.query('SELECT * FROM avatar WHERE account_id=$1 AND deleted=false', [account_id]) + const characters = await pool.query('SELECT a.*, b.* FROM avatar a LEFT JOIN avatarmodepermission b ON a.id = b.avatar_id WHERE a.account_id = $1 AND a.deleted = false', [account_id]) return characters.rows; } catch (e) { if (e.code) @@ -476,6 +501,31 @@ export async function update_account(account_id, fields) { } } +export async function update_roles(avatar_id, fields) { + if (fields === {}) { + return + } + + const set = build_SET(fields); + set.values.push(avatar_id) + const checkExists = await pool.query(`SELECT avatar_id FROM avatarmodepermission WHERE avatar_id = $1`, [avatar_id]); + + try { + let query; + if (checkExists.rowCount > 0) { + await pool.query(`UPDATE avatarmodepermission ${to_sql(set.sql)} WHERE avatar_id=$${set.next_idx}`, set.values); + } + else { + await pool.query(`INSERT INTO avatarmodepermission (avatar_id) VALUES ($1)`, [avatar_id]); + await pool.query(`UPDATE avatarmodepermission ${to_sql(set.sql)} WHERE avatar_id=$${set.next_idx}`, set.values); + } + } catch (e) { + if (e.code) + e.code = pg_error_inv[e.code] + throw e; + } +} + export async function get_empire_stats() { try { const query = await pool.query('SELECT faction_id, COUNT(*) FROM avatar GROUP BY faction_id'); @@ -571,8 +621,8 @@ export async function search(term, pagination) { const accounts = await pool.query('SELECT id, username, gm, inactive FROM account ' + 'WHERE upper(username) LIKE $1 ' + ` ORDER BY username OFFSET $2 LIMIT $3`, values); - const characters = await pool.query('SELECT id, name, account_id, faction_id FROM avatar ' + - 'WHERE upper(name) LIKE $1 ' + + const characters = await pool.query('SELECT a.id, a.name, a.account_id, a.faction_id, b.avatar_id, b.can_spectate, b.can_gm FROM avatar a' + + ' LEFT JOIN avatarmodepermission b ON a.id = b.avatar_id WHERE upper(a.name) LIKE $1 ' + ` ORDER BY name OFFSET $2 LIMIT $3`, values); pagination.item_count = 100; diff --git a/app/components/AccountLink.svelte b/app/components/AccountLink.svelte index c590f5d..59eba1d 100644 --- a/app/components/AccountLink.svelte +++ b/app/components/AccountLink.svelte @@ -19,6 +19,6 @@ {/if} {#if account.admin} - GM + Admin {/if} diff --git a/app/components/ActionButtons.svelte b/app/components/ActionButtons.svelte index 7f95816..d4872c1 100644 --- a/app/components/ActionButtons.svelte +++ b/app/components/ActionButtons.svelte @@ -25,7 +25,7 @@ data-account-id={account.id} data-account-name={account.name} data-toggle="modal" - data-target="#actionModal">Remove GM + data-target="#actionModal">Remove Admin {:else} + data-target="#actionModal">Make Admin {/if} {/if} diff --git a/app/components/CharacterLink.svelte b/app/components/CharacterLink.svelte index debb8c4..d02d9a6 100644 --- a/app/components/CharacterLink.svelte +++ b/app/components/CharacterLink.svelte @@ -11,6 +11,12 @@ {character.name} + {#if character.can_gm} + GM + {/if} + {#if character.can_spectate} + Spec + {/if} {:else} diff --git a/app/components/PermissionButtons.svelte b/app/components/PermissionButtons.svelte new file mode 100644 index 0000000..4b6fcbf --- /dev/null +++ b/app/components/PermissionButtons.svelte @@ -0,0 +1,38 @@ + + +{#if avatar.can_spectate} + +{:else} + +{/if} +{#if avatar.can_gm} + +{:else} + +{/if} diff --git a/app/components/RoleModal.svelte b/app/components/RoleModal.svelte new file mode 100644 index 0000000..499d38c --- /dev/null +++ b/app/components/RoleModal.svelte @@ -0,0 +1,74 @@ + + + diff --git a/app/views/AdminPanel.svelte b/app/views/AdminPanel.svelte index 1dd4496..8c00b68 100644 --- a/app/views/AdminPanel.svelte +++ b/app/views/AdminPanel.svelte @@ -2,6 +2,7 @@ import { onMount } from 'svelte' import UserList from '../views/UserList' import CharacterList from '../views/CharacterList' + import RoleList from '../views/RoleList' import CharacterLink from '../components/CharacterLink' import AccountLink from '../components/AccountLink' import { monitor_tabs } from '../util/navigation' @@ -41,6 +42,9 @@ +
@@ -74,4 +78,7 @@
+
+ +
diff --git a/app/views/CharacterList.svelte b/app/views/CharacterList.svelte index 51aa82e..429e936 100644 --- a/app/views/CharacterList.svelte +++ b/app/views/CharacterList.svelte @@ -6,7 +6,8 @@ import moment from 'moment' export let setURLParam = true; - export let appAlert + export let appAlert; + let characterList; async function fetch(page) { try { @@ -20,7 +21,7 @@ } - +

{pagination.item_count.toLocaleString()} characters in the database

diff --git a/app/views/Profile.svelte b/app/views/Profile.svelte index c10970e..209a005 100644 --- a/app/views/Profile.svelte +++ b/app/views/Profile.svelte @@ -10,7 +10,9 @@ import LoginList from '../components/LoginList' import AccountLink from '../components/AccountLink' import ActionButtons from '../components/ActionButtons' + import PermissionButtons from '../components/PermissionButtons' import ActionModal from '../components/ActionModal.svelte' + import RoleModal from '../components/RoleModal.svelte' export let pageCtx; export let appAlert; @@ -86,7 +88,7 @@ {#if characters.length >= 1}
{#each characters as char, i} -
+
{char.name}
{/each}
{:else} @@ -101,3 +103,4 @@ {/if} refresh()} /> + refresh()} /> diff --git a/app/views/RoleList.svelte b/app/views/RoleList.svelte new file mode 100644 index 0000000..a261149 --- /dev/null +++ b/app/views/RoleList.svelte @@ -0,0 +1,52 @@ + + + +
+

{pagination.item_count.toLocaleString()} characters in the database have a role assigned

+
+ + + + + + + + + + {#each roles as char, i} + + + + + + + {/each} + +
IDNameLast PlayedActions
#{char.id}{moment(char.last_login).fromNow()}
+
+ + roleList.refresh()} /> \ No newline at end of file diff --git a/app/views/UserList.svelte b/app/views/UserList.svelte index 6332e4d..1239e26 100644 --- a/app/views/UserList.svelte +++ b/app/views/UserList.svelte @@ -110,7 +110,7 @@