mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
feat: add weekdays condition to ABAC policies
This commit is contained in:
@@ -2762,7 +2762,15 @@
|
|||||||
"rateLimit": "Rate limit",
|
"rateLimit": "Rate limit",
|
||||||
"rateLimitValue": "Rate limit value",
|
"rateLimitValue": "Rate limit value",
|
||||||
"rateValue": "{{count}}/hr",
|
"rateValue": "{{count}}/hr",
|
||||||
"perHour": "actions per hour"
|
"perHour": "actions per hour",
|
||||||
|
"weekdays": "Days of week",
|
||||||
|
"day0": "Sun",
|
||||||
|
"day1": "Mon",
|
||||||
|
"day2": "Tue",
|
||||||
|
"day3": "Wed",
|
||||||
|
"day4": "Thu",
|
||||||
|
"day5": "Fri",
|
||||||
|
"day6": "Sat"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "Delete policy?",
|
"title": "Delete policy?",
|
||||||
|
|||||||
@@ -2548,7 +2548,15 @@
|
|||||||
"rateLimit": "محدودیت نرخ",
|
"rateLimit": "محدودیت نرخ",
|
||||||
"rateLimitValue": "مقدار محدودیت نرخ",
|
"rateLimitValue": "مقدار محدودیت نرخ",
|
||||||
"rateValue": "{{count}}/ساعت",
|
"rateValue": "{{count}}/ساعت",
|
||||||
"perHour": "عملیات در ساعت"
|
"perHour": "عملیات در ساعت",
|
||||||
|
"weekdays": "روزهای هفته",
|
||||||
|
"day0": "یکشنبه",
|
||||||
|
"day1": "دوشنبه",
|
||||||
|
"day2": "سهشنبه",
|
||||||
|
"day3": "چهارشنبه",
|
||||||
|
"day4": "پنجشنبه",
|
||||||
|
"day5": "جمعه",
|
||||||
|
"day6": "شنبه"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "حذف سیاست؟",
|
"title": "حذف سیاست؟",
|
||||||
|
|||||||
@@ -3313,7 +3313,15 @@
|
|||||||
"rateLimit": "Лимит запросов",
|
"rateLimit": "Лимит запросов",
|
||||||
"rateLimitValue": "Значение лимита запросов",
|
"rateLimitValue": "Значение лимита запросов",
|
||||||
"rateValue": "{{count}}/ч",
|
"rateValue": "{{count}}/ч",
|
||||||
"perHour": "действий в час"
|
"perHour": "действий в час",
|
||||||
|
"weekdays": "Дни недели",
|
||||||
|
"day0": "Вс",
|
||||||
|
"day1": "Пн",
|
||||||
|
"day2": "Вт",
|
||||||
|
"day3": "Ср",
|
||||||
|
"day4": "Чт",
|
||||||
|
"day5": "Пт",
|
||||||
|
"day6": "Сб"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "Удалить политику?",
|
"title": "Удалить политику?",
|
||||||
|
|||||||
@@ -2547,7 +2547,15 @@
|
|||||||
"rateLimit": "速率限制",
|
"rateLimit": "速率限制",
|
||||||
"rateLimitValue": "速率限制值",
|
"rateLimitValue": "速率限制值",
|
||||||
"rateValue": "{{count}}/小时",
|
"rateValue": "{{count}}/小时",
|
||||||
"perHour": "每小时操作数"
|
"perHour": "每小时操作数",
|
||||||
|
"weekdays": "星期",
|
||||||
|
"day0": "日",
|
||||||
|
"day1": "一",
|
||||||
|
"day2": "二",
|
||||||
|
"day3": "三",
|
||||||
|
"day4": "四",
|
||||||
|
"day5": "五",
|
||||||
|
"day6": "六"
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "删除策略?",
|
"title": "删除策略?",
|
||||||
|
|||||||
@@ -86,12 +86,23 @@ const BoltIcon = () => (
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const CalendarIcon = () => (
|
||||||
|
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
// === Helpers ===
|
// === Helpers ===
|
||||||
|
|
||||||
interface PolicyConditions {
|
interface PolicyConditions {
|
||||||
time_range?: { start: string; end: string };
|
time_range?: { start: string; end: string };
|
||||||
ip_whitelist?: string[];
|
ip_whitelist?: string[];
|
||||||
rate_limit?: number;
|
rate_limit?: number;
|
||||||
|
weekdays?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseConditions(raw: Record<string, unknown>): PolicyConditions {
|
function parseConditions(raw: Record<string, unknown>): PolicyConditions {
|
||||||
@@ -112,6 +123,10 @@ function parseConditions(raw: Record<string, unknown>): PolicyConditions {
|
|||||||
result.rate_limit = raw.rate_limit;
|
result.rate_limit = raw.rate_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(raw.weekdays)) {
|
||||||
|
result.weekdays = raw.weekdays.filter((d): d is number => typeof d === 'number');
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +254,22 @@ export default function AdminPolicies() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsed.weekdays && parsed.weekdays.length > 0 && parsed.weekdays.length < 7) {
|
||||||
|
const dayOrder = [1, 2, 3, 4, 5, 6, 0];
|
||||||
|
const sorted = dayOrder.filter((d) => parsed.weekdays!.includes(d));
|
||||||
|
const dayNames = sorted.map((d) => t(`admin.policies.conditions.day${d}`));
|
||||||
|
icons.push(
|
||||||
|
<span
|
||||||
|
key="weekdays"
|
||||||
|
className="inline-flex items-center gap-1 rounded bg-dark-700 px-1.5 py-0.5 text-xs text-dark-300"
|
||||||
|
title={t('admin.policies.conditions.weekdays')}
|
||||||
|
>
|
||||||
|
<CalendarIcon />
|
||||||
|
{dayNames.join(', ')}
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return icons;
|
return icons;
|
||||||
},
|
},
|
||||||
[t],
|
[t],
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface PolicyConditions {
|
|||||||
time_range?: { start: string; end: string };
|
time_range?: { start: string; end: string };
|
||||||
ip_whitelist?: string[];
|
ip_whitelist?: string[];
|
||||||
rate_limit?: number;
|
rate_limit?: number;
|
||||||
|
weekdays?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PolicyFormData {
|
interface PolicyFormData {
|
||||||
@@ -26,6 +27,7 @@ interface PolicyFormData {
|
|||||||
time_range: boolean;
|
time_range: boolean;
|
||||||
ip_whitelist: boolean;
|
ip_whitelist: boolean;
|
||||||
rate_limit: boolean;
|
rate_limit: boolean;
|
||||||
|
weekdays: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,11 +43,13 @@ const INITIAL_FORM: PolicyFormData = {
|
|||||||
time_range: { start: '09:00', end: '18:00' },
|
time_range: { start: '09:00', end: '18:00' },
|
||||||
ip_whitelist: [],
|
ip_whitelist: [],
|
||||||
rate_limit: 100,
|
rate_limit: 100,
|
||||||
|
weekdays: [1, 2, 3, 4, 5],
|
||||||
},
|
},
|
||||||
conditionsEnabled: {
|
conditionsEnabled: {
|
||||||
time_range: false,
|
time_range: false,
|
||||||
ip_whitelist: false,
|
ip_whitelist: false,
|
||||||
rate_limit: false,
|
rate_limit: false,
|
||||||
|
weekdays: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -69,6 +73,10 @@ function parseConditions(raw: Record<string, unknown>): PolicyConditions {
|
|||||||
result.rate_limit = raw.rate_limit;
|
result.rate_limit = raw.rate_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(raw.weekdays)) {
|
||||||
|
result.weekdays = raw.weekdays.filter((d): d is number => typeof d === 'number');
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +95,9 @@ function buildConditionsPayload(
|
|||||||
if (enabled.rate_limit && conditions.rate_limit !== undefined) {
|
if (enabled.rate_limit && conditions.rate_limit !== undefined) {
|
||||||
result.rate_limit = conditions.rate_limit;
|
result.rate_limit = conditions.rate_limit;
|
||||||
}
|
}
|
||||||
|
if (enabled.weekdays && conditions.weekdays && conditions.weekdays.length > 0) {
|
||||||
|
result.weekdays = conditions.weekdays;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -232,11 +243,13 @@ export default function AdminPolicyEdit() {
|
|||||||
time_range: parsed.time_range ?? { start: '09:00', end: '18:00' },
|
time_range: parsed.time_range ?? { start: '09:00', end: '18:00' },
|
||||||
ip_whitelist: parsed.ip_whitelist ?? [],
|
ip_whitelist: parsed.ip_whitelist ?? [],
|
||||||
rate_limit: parsed.rate_limit ?? 100,
|
rate_limit: parsed.rate_limit ?? 100,
|
||||||
|
weekdays: parsed.weekdays ?? [1, 2, 3, 4, 5],
|
||||||
},
|
},
|
||||||
conditionsEnabled: {
|
conditionsEnabled: {
|
||||||
time_range: !!parsed.time_range,
|
time_range: !!parsed.time_range,
|
||||||
ip_whitelist: !!parsed.ip_whitelist && parsed.ip_whitelist.length > 0,
|
ip_whitelist: !!parsed.ip_whitelist && parsed.ip_whitelist.length > 0,
|
||||||
rate_limit: parsed.rate_limit !== undefined,
|
rate_limit: parsed.rate_limit !== undefined,
|
||||||
|
weekdays: !!parsed.weekdays && parsed.weekdays.length > 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -687,6 +700,53 @@ export default function AdminPolicyEdit() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</ConditionToggle>
|
</ConditionToggle>
|
||||||
|
|
||||||
|
{/* Weekdays */}
|
||||||
|
<ConditionToggle
|
||||||
|
label={t('admin.policies.conditions.weekdays')}
|
||||||
|
enabled={formData.conditionsEnabled.weekdays}
|
||||||
|
onToggle={() =>
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
conditionsEnabled: {
|
||||||
|
...prev.conditionsEnabled,
|
||||||
|
weekdays: !prev.conditionsEnabled.weekdays,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex flex-wrap gap-1.5">
|
||||||
|
{[1, 2, 3, 4, 5, 6, 0].map((day) => {
|
||||||
|
const selected = (formData.conditions.weekdays ?? []).includes(day);
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={day}
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
setFormData((prev) => {
|
||||||
|
const current = prev.conditions.weekdays ?? [];
|
||||||
|
const next = selected
|
||||||
|
? current.filter((d) => d !== day)
|
||||||
|
: [...current, day];
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
conditions: { ...prev.conditions, weekdays: next },
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className={`rounded-lg px-3 py-1.5 text-xs font-medium transition-colors ${
|
||||||
|
selected
|
||||||
|
? 'bg-accent-500/20 text-accent-400'
|
||||||
|
: 'bg-dark-700/50 text-dark-400 hover:bg-dark-700 hover:text-dark-300'
|
||||||
|
}`}
|
||||||
|
aria-pressed={selected}
|
||||||
|
>
|
||||||
|
{t(`admin.policies.conditions.day${day}`)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ConditionToggle>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user