rate limits page
This commit is contained in:
parent
576774540f
commit
57dfbd8a53
8 changed files with 285 additions and 8 deletions
20
src/components/settings_modal/admin_tabs/rates_tab.js
Normal file
20
src/components/settings_modal/admin_tabs/rates_tab.js
Normal 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
|
||||
41
src/components/settings_modal/admin_tabs/rates_tab.vue
Normal file
41
src/components/settings_modal/admin_tabs/rates_tab.vue
Normal 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>
|
||||
53
src/components/settings_modal/helpers/rate_setting.js
Normal file
53
src/components/settings_modal/helpers/rate_setting.js
Normal 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]]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/components/settings_modal/helpers/rate_setting.vue
Normal file
117
src/components/settings_modal/helpers/rate_setting.vue
Normal 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> </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>
|
||||
|
|
@ -13,6 +13,7 @@ import RegistrationsTab from './admin_tabs/registrations_tab.vue'
|
|||
import AuthTab from './admin_tabs/auth_tab.vue'
|
||||
import HTTPTab from './admin_tabs/http_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 FederationTab from './admin_tabs/federation_tab.vue'
|
||||
import JobQueuesTab from './admin_tabs/job_queues_tab.vue'
|
||||
|
|
@ -34,7 +35,8 @@ import {
|
|||
faCircleNodes,
|
||||
faUpload,
|
||||
faMessage,
|
||||
faEllipsis
|
||||
faEllipsis,
|
||||
faGauge
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
|
@ -52,7 +54,8 @@ library.add(
|
|||
faCircleNodes,
|
||||
faUpload,
|
||||
faMessage,
|
||||
faEllipsis
|
||||
faEllipsis,
|
||||
faGauge
|
||||
)
|
||||
|
||||
const SettingsModalAdminContent = {
|
||||
|
|
@ -73,6 +76,7 @@ const SettingsModalAdminContent = {
|
|||
AuthTab,
|
||||
HTTPTab,
|
||||
MonitoringTab,
|
||||
RatesTab,
|
||||
OtherTab,
|
||||
PostsTab
|
||||
},
|
||||
|
|
|
|||
|
|
@ -92,6 +92,15 @@
|
|||
<LimitsTab />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="adminDbLoaded"
|
||||
:label="$t('admin_dash.tabs.rate_limit')"
|
||||
icon="gauge"
|
||||
data-tab-name="rate_limits"
|
||||
>
|
||||
<RatesTab />
|
||||
</div>
|
||||
|
||||
<div
|
||||
:label="$t('admin_dash.tabs.uploads')"
|
||||
icon="upload"
|
||||
|
|
|
|||
|
|
@ -1162,6 +1162,7 @@
|
|||
"job_queues": "Job Queues",
|
||||
"auth": "Auth",
|
||||
"posts": "Posts",
|
||||
"rate_limit": "Rate Limits",
|
||||
"http": "HTTP",
|
||||
"federation": "Federation",
|
||||
"other": "Other"
|
||||
|
|
@ -1227,6 +1228,14 @@
|
|||
},
|
||||
"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": {
|
||||
"heading": "Database config is disabled",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
":rate_limit": {
|
||||
":oauth_app_creation": {
|
||||
"label": "OAuth app creation",
|
||||
"description": "For registering new OAuth App ID"
|
||||
}
|
||||
},
|
||||
"Pleroma_DOT_Formatter": {
|
||||
":attribute_toggle": {
|
||||
"label": "Set {attr} attribute"
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ const adminSettingsStorage = {
|
|||
setInstanceAdminSettings ({ state, commit }, { backendDbConfig }) {
|
||||
const config = state.config || {}
|
||||
const modifiedPaths = new Set()
|
||||
|
||||
backendDbConfig.configs.forEach(c => {
|
||||
const path = [c.group, c.key]
|
||||
if (c.db) {
|
||||
|
|
@ -94,16 +95,33 @@ const adminSettingsStorage = {
|
|||
// Using strings for modified paths for easier searching
|
||||
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) {
|
||||
return value.reduce((acc, c) => {
|
||||
return { ...acc, [c.tuple[0]]: convert(c.tuple[1]) }
|
||||
}, {})
|
||||
if (!preserveTuples) {
|
||||
return value.reduce((acc, c) => {
|
||||
return { ...acc, [c.tuple[0]]: convert(c.tuple[1], preserveTuplesLv2) }
|
||||
}, {})
|
||||
} else {
|
||||
return value.map(x => x.tuple)
|
||||
}
|
||||
} else {
|
||||
return value
|
||||
if (!preserveTuples) {
|
||||
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)))
|
||||
// patching http adapter config to be easier to handle
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue