Add interface to block ip-addresses or clients due to abuse (#15484)

* Read IP blocks from database

* begin building general blocking solution

* add new frontend files

* Add UI for managing blockers

* correctly reset local data after creating blocker

* Tweak wording

* Add UI for managing blockers

* restructure admin pages

* improve test coverage

* Improve blocker UI

* add blocker to block emails from registration

* lint fix

* fix

* lint fixes

* fix import

* add new permission for managing blockers

* improve permission check

* fix managing permissions from admin

* improve navbar display for non fullAccess admin

* update block error strings

* lint fix

* add option to errorHandler to skip logging

* validate blocker value during input

* improve blocker form display

* chore(subproj): reconcile habitica-images

* fix(scripts): use same Mongo version for dev/test

* fix(whitespace): eof

* documentation improvements

* remove nconf import

* remove old test

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
This commit is contained in:
Phillip Thelen
2025-08-06 22:08:07 +02:00
committed by GitHub
parent ae4130b108
commit 12773d539e
51 changed files with 1454 additions and 428 deletions

View File

@@ -0,0 +1,73 @@
<template>
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="expand = !expand"
>
Current Avatar Appearance, Drop Count Today
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<div>Drops Today: {{ items.lastDrop.count }}</div>
<div>Most Recent Drop: {{ items.lastDrop.date | formatDate }}</div>
<div>Use Costume: {{ preferences.costume ? 'on' : 'off' }}</div>
<div class="subsection-start">
Equipped Gear:
<ul v-html="formatEquipment(items.gear.equipped)"></ul>
</div>
<div>
Costume:
<ul v-html="formatEquipment(items.gear.costume)"></ul>
</div>
</div>
</div>
</template>
<script>
import formatDate from '../filters/formatDate';
import getItemDescription from '../mixins/getItemDescription';
export default {
filters: {
formatDate,
},
mixins: [
getItemDescription,
],
props: {
items: {
type: Object,
required: true,
},
preferences: {
type: Object,
required: true,
},
},
data () {
return {
expand: false,
};
},
methods: {
formatEquipment (gearWorn) {
const gearTypes = ['head', 'armor', 'weapon', 'shield', 'headAccessory', 'eyewear',
'body', 'back'];
let equipmentList = '';
gearTypes.forEach(gearType => {
const key = gearWorn[gearType] || '';
const description = (key)
? `<strong>${key}</strong> : ${this.getItemDescription('gear', gearWorn[gearType])}`
: 'none';
equipmentList += `<li>${gearType} : ${description}</li>\n`;
});
return equipmentList;
},
},
};
</script>