Merge branch 'admin-tabs-2' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2025-12-09 13:24:57 +02:00
commit 03e9e5082f
8 changed files with 285 additions and 8 deletions

View file

@ -0,0 +1,20 @@
import RateSetting from '../helpers/rate_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const RatesTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
RateSetting,
},
computed: {
...SharedComputedObject()
}
}
export default RatesTab

View file

@ -0,0 +1,41 @@
<template>
<div :label="$t('admin_dash.tabs.instance')">
<div class="setting-item">
<h3>{{ $t('admin_dash.rate_limit.account_confirmation_resend') }}</h3>
<ul class="setting-list">
<li>
<RateSetting path=":pleroma.:rate_limit.:account_confirmation_resend" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:ap_routes" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:app_account_creation" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:authentication" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:oauth_app_creation" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:relation_id_action" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:search" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:status_id_action" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:statuses_actions" />
</li>
<li>
<RateSetting path=":pleroma.:rate_limit.:timeline" />
</li>
</ul>
</div>
</div>
</template>
<script src="./rates_tab.js"></script>

View file

@ -0,0 +1,53 @@
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Setting from './setting.js'
export default {
...Setting,
data () {
return {
newValue: '',
}
},
components: {
...Setting.components,
Checkbox
},
props: {
...Setting.props
},
computed: {
...Setting.computed,
isSeparate () {
// [[a1, b1], [a2, b2]] vs [a, b]
return Array.isArray(this.visibleState[0])
},
normalizedState () {
if (this.isSeparate) {
return this.visibleState.map(y => y.map(x => Number(x) || 0))
} else {
return [this.visibleState.map(x => Number(x) || 0)]
}
}
},
methods: {
...Setting.methods,
getValue ({ event, side, index, eventType }) {
if (eventType === 'edit') {
const value = Number(event.target.value)
if (Number.isNaN(value)) return this.visibleState
const newVal = [...this.normalizedState.map(x => [...x])]
newVal[side][index] = value
return newVal
}
if (eventType === 'toggleMode') {
if (event === 'split') {
return [this.normalizedState[0], this.normalizedState[0]]
} else if (event === 'join') {
return [this.normalizedState[0]]
}
}
}
}
}

View file

@ -0,0 +1,117 @@
<template>
<div
v-if="matchesExpertLevel"
class="RateSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<table>
<tr>
<th>&nbsp;</th>
<th>
{{ $t('admin_dash.rate_limit.period') }}
</th>
<th>
{{ $t('admin_dash.rate_limit.amount') }}
</th>
</tr>
<tr>
<td v-if="isSeparate">
{{ $t('admin_dash.rate_limit.unauthenticated') }}
</td>
<td v-else>
{{ $t('admin_dash.rate_limit.rate_limit') }}
</td>
<td>
<input
class="input string-input"
type="number"
:value="normalizedState[0][0]"
@change="e => update({ event: e, index: 0, side: 0, eventType: 'edit' })"
>
</td>
<td>
<input
class="input string-input"
type="number"
:value="normalizedState[0][1]"
@change="e => update({ event: e, index: 1, side: 0, eventType: 'edit' })"
>
</td>
</tr>
<tr v-if="isSeparate">
<td>
{{ $t('admin_dash.rate_limit.authenticated') }}
</td>
<td>
<input
class="input string-input"
type="number"
:value="normalizedState[1][0]"
@change="e => update({ event: e, index: 0, side: 1, eventType: 'edit' })"
>
</td>
<td>
<input
class="input string-input"
type="number"
:value="normalizedState[1][1]"
@change="e => update({ event: e, index: 1, side: 1, eventType: 'edit' })"
>
</td>
</tr>
</table>
<Checkbox
:model-value="isSeparate"
@update:model-value="event => update({ event: event ? 'join' : 'split', eventType: 'toggleMode' })"
>
{{ $t('admin_dash.rate_limit.separate') }}
</Checkbox>
<div>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</div>
</template>
<script src="./rate_setting.js"></script>
<style lang="scss">
.RateSetting {
table {
margin-top: 0.5em;
}
th {
font-weight: normal;
}
td {
input {
width: 15ch;
}
}
margin-bottom: 2em;
}
</style>

View file

@ -13,6 +13,7 @@ import RegistrationsTab from './admin_tabs/registrations_tab.vue'
import AuthTab from './admin_tabs/auth_tab.vue' import AuthTab from './admin_tabs/auth_tab.vue'
import HTTPTab from './admin_tabs/http_tab.vue' import HTTPTab from './admin_tabs/http_tab.vue'
import OtherTab from './admin_tabs/other_tab.vue' import OtherTab from './admin_tabs/other_tab.vue'
import RatesTab from './admin_tabs/rates_tab.vue'
import PostsTab from './admin_tabs/posts_tab.vue' import PostsTab from './admin_tabs/posts_tab.vue'
import FederationTab from './admin_tabs/federation_tab.vue' import FederationTab from './admin_tabs/federation_tab.vue'
import JobQueuesTab from './admin_tabs/job_queues_tab.vue' import JobQueuesTab from './admin_tabs/job_queues_tab.vue'
@ -34,7 +35,8 @@ import {
faCircleNodes, faCircleNodes,
faUpload, faUpload,
faMessage, faMessage,
faEllipsis faEllipsis,
faGauge
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
library.add( library.add(
@ -52,7 +54,8 @@ library.add(
faCircleNodes, faCircleNodes,
faUpload, faUpload,
faMessage, faMessage,
faEllipsis faEllipsis,
faGauge
) )
const SettingsModalAdminContent = { const SettingsModalAdminContent = {
@ -73,6 +76,7 @@ const SettingsModalAdminContent = {
AuthTab, AuthTab,
HTTPTab, HTTPTab,
MonitoringTab, MonitoringTab,
RatesTab,
OtherTab, OtherTab,
PostsTab PostsTab
}, },

View file

@ -92,6 +92,15 @@
<LimitsTab /> <LimitsTab />
</div> </div>
<div
v-if="adminDbLoaded"
:label="$t('admin_dash.tabs.rate_limit')"
icon="gauge"
data-tab-name="rate_limits"
>
<RatesTab />
</div>
<div <div
:label="$t('admin_dash.tabs.uploads')" :label="$t('admin_dash.tabs.uploads')"
icon="upload" icon="upload"

View file

@ -1162,6 +1162,7 @@
"job_queues": "Job Queues", "job_queues": "Job Queues",
"auth": "Auth", "auth": "Auth",
"posts": "Posts", "posts": "Posts",
"rate_limit": "Rate Limits",
"http": "HTTP", "http": "HTTP",
"federation": "Federation", "federation": "Federation",
"other": "Other" "other": "Other"
@ -1227,6 +1228,14 @@
}, },
"queues": "Queues" "queues": "Queues"
}, },
"rate_limit": {
"rate_limit": "Rate Limit",
"amount": "Amount",
"unauthenticated": "Unauthenticated",
"authenticated": "Authenticated",
"period": "Time period",
"separate": "Separate rate limits for authenticated/unauthenticated users"
},
"nodb": { "nodb": {
"heading": "Database config is disabled", "heading": "Database config is disabled",
"text": "You need to change backend config files so that {property} is set to {value}, see more in {documentation}.", "text": "You need to change backend config files so that {property} is set to {value}, see more in {documentation}.",
@ -1421,6 +1430,12 @@
"description": "idk" "description": "idk"
} }
}, },
":rate_limit": {
":oauth_app_creation": {
"label": "OAuth app creation",
"description": "For registering new OAuth App ID"
}
},
"Pleroma_DOT_Formatter": { "Pleroma_DOT_Formatter": {
":attribute_toggle": { ":attribute_toggle": {
"label": "Set {attr} attribute" "label": "Set {attr} attribute"

View file

@ -87,6 +87,7 @@ const adminSettingsStorage = {
setInstanceAdminSettings ({ state, commit }, { backendDbConfig }) { setInstanceAdminSettings ({ state, commit }, { backendDbConfig }) {
const config = state.config || {} const config = state.config || {}
const modifiedPaths = new Set() const modifiedPaths = new Set()
backendDbConfig.configs.forEach(c => { backendDbConfig.configs.forEach(c => {
const path = [c.group, c.key] const path = [c.group, c.key]
if (c.db) { if (c.db) {
@ -94,16 +95,33 @@ const adminSettingsStorage = {
// Using strings for modified paths for easier searching // Using strings for modified paths for easier searching
c.db.forEach(x => modifiedPaths.add([...path, x].join(' -> '))) c.db.forEach(x => modifiedPaths.add([...path, x].join(' -> ')))
} }
const convert = (value) => {
// we need to preserve tuples on second level only, possibly third
// but it's not a case right now.
const convert = (value, preserveTuples, preserveTuplesLv2) => {
if (Array.isArray(value) && value.length > 0 && value[0].tuple) { if (Array.isArray(value) && value.length > 0 && value[0].tuple) {
if (!preserveTuples) {
return value.reduce((acc, c) => { return value.reduce((acc, c) => {
return { ...acc, [c.tuple[0]]: convert(c.tuple[1]) } return { ...acc, [c.tuple[0]]: convert(c.tuple[1], preserveTuplesLv2) }
}, {}) }, {})
} else { } else {
return value.map(x => x.tuple)
}
} else {
if (!preserveTuples) {
return value return value
} else {
return value.tuple
} }
} }
set(config, path, convert(c.value)) }
// for most stuff we want maps since those are more convenient
// however this doesn't allow for multiple values per same key
// so for those cases we want to preserve tuples as-is
// right now it's made exclusively for :pleroma.:rate_limit
// so it might not work properly elsewhere
const preserveTuples = path.find(x => x === ':rate_limit')
set(config, path, convert(c.value, false, preserveTuples))
}) })
console.log('CONFIG', JSON.parse(JSON.stringify(config))) console.log('CONFIG', JSON.parse(JSON.stringify(config)))
// patching http adapter config to be easier to handle // patching http adapter config to be easier to handle