mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
Merge pull request #532 from RWDai/opencode/cosmic-nebula
fix: raise group rate limits by access tier
This commit is contained in:
@@ -2000,7 +2000,7 @@ fn resolve_effective_rate_limit_policy(
|
|||||||
groups: &[aether_data::repository::users::StoredUserGroup],
|
groups: &[aether_data::repository::users::StoredUserGroup],
|
||||||
) -> Option<i32> {
|
) -> Option<i32> {
|
||||||
let group_policy = groups.iter().fold(None, |effective, group| {
|
let group_policy = groups.iter().fold(None, |effective, group| {
|
||||||
intersect_rate_limit_policies(
|
union_rate_limit_policies(
|
||||||
effective,
|
effective,
|
||||||
rate_limit_restriction_from_mode(&group.rate_limit_mode, group.rate_limit),
|
rate_limit_restriction_from_mode(&group.rate_limit_mode, group.rate_limit),
|
||||||
)
|
)
|
||||||
@@ -2073,6 +2073,22 @@ fn intersect_rate_limit_policies(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn union_rate_limit_policies(
|
||||||
|
left: Option<RateLimitRestriction>,
|
||||||
|
right: Option<RateLimitRestriction>,
|
||||||
|
) -> Option<RateLimitRestriction> {
|
||||||
|
match (left, right) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(value), None) | (None, Some(value)) => Some(value),
|
||||||
|
(Some(RateLimitRestriction::Unlimited), _) | (_, Some(RateLimitRestriction::Unlimited)) => {
|
||||||
|
Some(RateLimitRestriction::Unlimited)
|
||||||
|
}
|
||||||
|
(Some(RateLimitRestriction::Limited(left)), Some(RateLimitRestriction::Limited(right))) => {
|
||||||
|
Some(RateLimitRestriction::Limited(left.max(right)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rate_limit_policy_value(policy: Option<RateLimitRestriction>) -> Option<i32> {
|
fn rate_limit_policy_value(policy: Option<RateLimitRestriction>) -> Option<i32> {
|
||||||
match policy {
|
match policy {
|
||||||
None => None,
|
None => None,
|
||||||
@@ -2358,35 +2374,44 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rate_limit_policy_uses_most_restrictive_custom_limit() {
|
fn rate_limit_policy_uses_highest_group_limit_before_user_restriction() {
|
||||||
let groups = vec![sample_group(
|
let groups = vec![
|
||||||
"restricted",
|
sample_group("default", 10, None, "unrestricted", Some(30), "custom"),
|
||||||
10,
|
sample_group("tier-1", 20, None, "unrestricted", Some(100), "custom"),
|
||||||
None,
|
];
|
||||||
"unrestricted",
|
|
||||||
Some(60),
|
|
||||||
"custom",
|
|
||||||
)];
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_effective_rate_limit_policy(Some(120), "custom", &groups),
|
resolve_effective_rate_limit_policy(Some(120), "custom", &groups),
|
||||||
Some(60)
|
Some(100)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rate_limit_unlimited_does_not_bypass_limited_group() {
|
fn rate_limit_unlimited_group_overrides_limited_groups() {
|
||||||
|
let groups = vec![
|
||||||
|
sample_group("default", 10, None, "unrestricted", Some(30), "custom"),
|
||||||
|
sample_group("tier-2", 20, None, "unrestricted", Some(0), "custom"),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
resolve_effective_rate_limit_policy(None, "system", &groups),
|
||||||
|
Some(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rate_limit_user_policy_still_restricts_group_grants() {
|
||||||
let groups = vec![sample_group(
|
let groups = vec![sample_group(
|
||||||
"restricted",
|
"tier-1",
|
||||||
10,
|
10,
|
||||||
None,
|
None,
|
||||||
"unrestricted",
|
"unrestricted",
|
||||||
Some(60),
|
Some(100),
|
||||||
"custom",
|
"custom",
|
||||||
)];
|
)];
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_effective_rate_limit_policy(Some(0), "custom", &groups),
|
resolve_effective_rate_limit_policy(Some(60), "custom", &groups),
|
||||||
Some(60)
|
Some(60)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,8 +128,25 @@
|
|||||||
<div class="space-y-4 border-t border-border/60 pt-5">
|
<div class="space-y-4 border-t border-border/60 pt-5">
|
||||||
<div class="flex flex-wrap items-baseline justify-between gap-x-2 gap-y-1 pb-2 border-b border-border/60">
|
<div class="flex flex-wrap items-baseline justify-between gap-x-2 gap-y-1 pb-2 border-b border-border/60">
|
||||||
<span class="text-sm font-medium">组权限</span>
|
<span class="text-sm font-medium">组权限</span>
|
||||||
<span class="text-[11px] text-muted-foreground">
|
<span class="flex items-center gap-1 text-[11px] text-muted-foreground">
|
||||||
多个组与用户额外限制取交集
|
组权限叠加,Key 可再收窄
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="inline-flex h-4 w-4 items-center justify-center rounded-full border border-border/70 bg-muted/40 text-muted-foreground outline-none transition-colors hover:border-primary/50 hover:text-primary focus-visible:border-primary/60 focus-visible:text-primary"
|
||||||
|
:title="groupPolicyHelpText"
|
||||||
|
aria-label="查看组权限合并规则"
|
||||||
|
>
|
||||||
|
<Info class="h-3 w-3" />
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent class="max-w-72 text-xs leading-5">
|
||||||
|
{{ groupPolicyHelpText }}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -247,7 +264,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { BadgeCheck, ChevronRight, Plus, Trash2 } from 'lucide-vue-next'
|
import { BadgeCheck, ChevronRight, Info, Plus, Trash2 } from 'lucide-vue-next'
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
@@ -255,6 +272,10 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Label,
|
Label,
|
||||||
Switch,
|
Switch,
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
} from '@/components/ui'
|
} from '@/components/ui'
|
||||||
import { MultiSelect } from '@/components/common'
|
import { MultiSelect } from '@/components/common'
|
||||||
import { useUsersStore } from '@/stores/users'
|
import { useUsersStore } from '@/stores/users'
|
||||||
@@ -302,6 +323,8 @@ const USER_OPTIONS_CACHE_TTL_MS = 30 * 1000
|
|||||||
let dialogUsersLoadedAt = 0
|
let dialogUsersLoadedAt = 0
|
||||||
let dialogUsersLoadedVersion = -1
|
let dialogUsersLoadedVersion = -1
|
||||||
|
|
||||||
|
const groupPolicyHelpText = '模型、供应商和端点会在多个用户组之间叠加授权;unrestricted 仍表示不限制,deny_all 只是不授予额外权限。速率限制按付费档位取更高额度,0 表示不限速;用户/API Key 自身限制仍会收窄最终权限。'
|
||||||
|
|
||||||
const form = ref({
|
const form = ref({
|
||||||
name: '',
|
name: '',
|
||||||
allowed_providers_mode: 'unrestricted' as ListPolicyMode,
|
allowed_providers_mode: 'unrestricted' as ListPolicyMode,
|
||||||
|
|||||||
Reference in New Issue
Block a user