feat: add admin HWID device limit overrides
This commit is contained in:
@@ -73,6 +73,23 @@
|
||||
return trial.active ? `${base} · ${at("user_trial_active", {}, "активен")}` : base;
|
||||
}
|
||||
|
||||
function hwidLimitLabel(sub) {
|
||||
const rawBase = sub?.hwid_device_limit;
|
||||
const hasBase = rawBase !== null && rawBase !== undefined;
|
||||
const extra = Math.max(0, Number(sub?.extra_hwid_devices || 0));
|
||||
if (!hasBase) return at("user_hwid_limit_default", {}, "Тарифный / default");
|
||||
const base = Number(rawBase);
|
||||
if (base === 0) return at("user_hwid_limit_unlimited", {}, "Безлимит");
|
||||
if (extra > 0) {
|
||||
return at(
|
||||
"user_hwid_limit_with_extra",
|
||||
{ base, extra, total: base + extra },
|
||||
`${base + extra} (${base} + ${extra})`
|
||||
);
|
||||
}
|
||||
return at("user_hwid_limit_count", { count: base }, `${base}`);
|
||||
}
|
||||
|
||||
const usersStore = getContext("usersStore");
|
||||
|
||||
$: ({
|
||||
@@ -91,6 +108,7 @@
|
||||
userReferralsPage,
|
||||
userReferralsPageSize,
|
||||
premiumUnlimitedDraft,
|
||||
hwidUnlimitedDraft,
|
||||
userDetailTab,
|
||||
userLogs,
|
||||
userLogsTotal,
|
||||
@@ -437,6 +455,11 @@
|
||||
>{openedUserDetail.active_subscription.provider || "—"}</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span>{at("user_label_hwid_devices", {}, "HWID-устройства")}</span><strong
|
||||
>{hwidLimitLabel(openedUserDetail.active_subscription)}</strong
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="admin-traffic-summary">
|
||||
<AdminTrafficCard
|
||||
@@ -897,6 +920,66 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="admin-user-action-sheet admin-user-action-sheet--hwid-limit">
|
||||
<AdminSectionHeader
|
||||
title={at("user_hwid_limit_card_title", {}, "HWID-устройства")}
|
||||
description={at(
|
||||
"user_hwid_limit_card_hint",
|
||||
{},
|
||||
"Ручной лимит устройств для пользователя. Пустое поле вернёт тарифный или default-лимит."
|
||||
)}
|
||||
/>
|
||||
<div class="admin-user-action-sheet-body admin-user-override-stack">
|
||||
<Label.Root class="admin-field-label admin-extend-field">
|
||||
<span>{at("user_hwid_limit_input", {}, "Лимит устройств")}</span>
|
||||
<small
|
||||
>{at(
|
||||
"user_hwid_limit_input_hint",
|
||||
{},
|
||||
"Пусто — тариф/default; 0 или галочка — безлимит."
|
||||
)}</small
|
||||
>
|
||||
<Input
|
||||
class="input"
|
||||
type="number"
|
||||
min="0"
|
||||
step="1"
|
||||
placeholder={at("user_hwid_limit_default_placeholder", {}, "Тариф")}
|
||||
disabled={hwidUnlimitedDraft}
|
||||
aria-label={at("user_hwid_limit_input", {}, "Лимит устройств")}
|
||||
bind:value={$usersStore.hwidDeviceLimitDraft}
|
||||
/>
|
||||
</Label.Root>
|
||||
</div>
|
||||
<div class="admin-user-action-sheet-footer admin-override-card-footer">
|
||||
<div class="admin-override-card-toolbar">
|
||||
<label class="admin-override-unlimited-label">
|
||||
<Checkbox
|
||||
bind:checked={$usersStore.hwidUnlimitedDraft}
|
||||
aria-label={at("user_override_unlimited_short", {}, "Безлимит")}
|
||||
/>
|
||||
<span>{at("user_override_unlimited_short", {}, "Безлимит")}</span>
|
||||
</label>
|
||||
<AdminButton
|
||||
variant="primary"
|
||||
onclick={usersStore.saveHwidDeviceLimit}
|
||||
disabled={userActionBusy}
|
||||
>
|
||||
{at("user_hwid_limit_save", {}, "Сохранить")}
|
||||
</AdminButton>
|
||||
</div>
|
||||
<div class="admin-override-status-lines">
|
||||
<span class="admin-meta-truncate">
|
||||
{at(
|
||||
"user_hwid_limit_status",
|
||||
{ current: hwidLimitLabel(openedUserDetail.active_subscription) },
|
||||
`Сейчас: ${hwidLimitLabel(openedUserDetail.active_subscription)}`
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="admin-user-action-sheet admin-user-action-sheet--traffic-grant">
|
||||
<AdminSectionHeader
|
||||
title={at("user_traffic_grant_title", {}, "Выдать трафик")}
|
||||
|
||||
@@ -37,6 +37,8 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
premiumBonusGbDraft: "",
|
||||
regularUnlimitedDraft: false,
|
||||
regularBonusGbDraft: "",
|
||||
hwidUnlimitedDraft: false,
|
||||
hwidDeviceLimitDraft: "",
|
||||
grantTrafficGbDraft: "",
|
||||
grantTrafficKindDraft: "regular",
|
||||
|
||||
@@ -154,6 +156,9 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
const sub = res.active_subscription || null;
|
||||
const bonusBytes = Number(sub?.premium_bonus_bytes || 0);
|
||||
const regularBonusBytes = Number(sub?.regular_bonus_bytes || 0);
|
||||
const hasHwidLimit =
|
||||
sub?.hwid_device_limit !== null && sub?.hwid_device_limit !== undefined;
|
||||
const hwidLimit = hasHwidLimit ? Number(sub?.hwid_device_limit) : null;
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
openedUserDetail: res,
|
||||
@@ -163,6 +168,8 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
regularUnlimitedDraft: Boolean(sub?.regular_unlimited_override),
|
||||
regularBonusGbDraft:
|
||||
regularBonusBytes > 0 ? +(regularBonusBytes / 1024 ** 3).toFixed(2) : "",
|
||||
hwidUnlimitedDraft: hasHwidLimit && hwidLimit === 0,
|
||||
hwidDeviceLimitDraft: hasHwidLimit && hwidLimit > 0 ? String(hwidLimit) : "",
|
||||
grantTrafficGbDraft: "",
|
||||
grantTrafficKindDraft: "regular",
|
||||
}));
|
||||
@@ -551,6 +558,51 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveHwidDeviceLimit() {
|
||||
let s;
|
||||
state.update((st) => {
|
||||
s = st;
|
||||
return st;
|
||||
});
|
||||
if (!s.openedUser) return;
|
||||
state.update((st) => ({ ...st, userActionBusy: true }));
|
||||
try {
|
||||
const unlimited = Boolean(s.hwidUnlimitedDraft);
|
||||
const raw = s.hwidDeviceLimitDraft;
|
||||
const useDefault = !unlimited && (raw === "" || raw === null || raw === undefined);
|
||||
let limit = null;
|
||||
if (!unlimited && !useDefault) {
|
||||
limit = Number(raw);
|
||||
if (!Number.isInteger(limit) || limit < 0 || limit > 1_000_000) {
|
||||
onToast(
|
||||
at(
|
||||
"hwid_limit_invalid",
|
||||
{},
|
||||
"Введите целое число устройств от 0 до 1 000 000 или включите безлимит"
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const res = await api(`/admin/users/${s.openedUser.user_id}/hwid-device-limit`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
unlimited,
|
||||
use_default: useDefault,
|
||||
hwid_device_limit: unlimited ? 0 : limit,
|
||||
}),
|
||||
});
|
||||
if (res?.ok) {
|
||||
onToast(at("hwid_limit_saved", {}, "Лимит устройств сохранён"));
|
||||
await openUser(s.openedUser, { skipPush: true });
|
||||
} else {
|
||||
onToast(res?.error || at("error", {}, "Ошибка"));
|
||||
}
|
||||
} finally {
|
||||
state.update((st) => ({ ...st, userActionBusy: false }));
|
||||
}
|
||||
}
|
||||
|
||||
async function grantTraffic() {
|
||||
let s;
|
||||
state.update((st) => {
|
||||
@@ -635,6 +687,7 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
deleteUser,
|
||||
savePremiumTrafficOverride,
|
||||
saveRegularTrafficOverride,
|
||||
saveHwidDeviceLimit,
|
||||
grantTraffic,
|
||||
loadUserLogs,
|
||||
setUserLogsPage,
|
||||
|
||||
@@ -14,7 +14,7 @@ export const DEMO_DATASET = {
|
||||
payments: 482,
|
||||
logs: 1600,
|
||||
supportTickets: 3,
|
||||
translationKeys: 1961,
|
||||
translationKeys: 1994,
|
||||
settingsFields: 223,
|
||||
},
|
||||
},
|
||||
@@ -11088,8 +11088,8 @@ export const DEMO_DATASET = {
|
||||
provider: "trial",
|
||||
is_throttled: false,
|
||||
install_share_token: "1a0d471f37fdbd7429931a9a75d6e221",
|
||||
hwid_device_limit: 0,
|
||||
extra_hwid_devices: 0,
|
||||
hwid_device_limit: 4,
|
||||
extra_hwid_devices: 2,
|
||||
},
|
||||
subscriptions: [
|
||||
{
|
||||
@@ -11122,8 +11122,8 @@ export const DEMO_DATASET = {
|
||||
provider: "trial",
|
||||
is_throttled: false,
|
||||
install_share_token: "1a0d471f37fdbd7429931a9a75d6e221",
|
||||
hwid_device_limit: 0,
|
||||
extra_hwid_devices: 0,
|
||||
hwid_device_limit: 4,
|
||||
extra_hwid_devices: 2,
|
||||
},
|
||||
],
|
||||
total_paid: 0,
|
||||
@@ -105180,6 +105180,812 @@ export const DEMO_DATASET = {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_user_hwid_limit_button",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "📱 HWID лимит",
|
||||
fallback: "📱 HWID лимит",
|
||||
effective: "📱 HWID лимит",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "📱 HWID limit",
|
||||
fallback: "📱 HWID лимит",
|
||||
effective: "📱 HWID limit",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_user_hwid_limit_label",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "📱 HWID-устройства:",
|
||||
fallback: "📱 HWID-устройства:",
|
||||
effective: "📱 HWID-устройства:",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "📱 HWID devices:",
|
||||
fallback: "📱 HWID-устройства:",
|
||||
effective: "📱 HWID devices:",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_title",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "📱 Лимит HWID-устройств",
|
||||
fallback: "📱 Лимит HWID-устройств",
|
||||
effective: "📱 Лимит HWID-устройств",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "📱 HWID device limit",
|
||||
fallback: "📱 Лимит HWID-устройств",
|
||||
effective: "📱 HWID device limit",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_hint",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Задайте ручной базовый лимит устройств для пользователя. 0 означает безлимит; сброс вернёт тарифный или default-лимит.",
|
||||
fallback:
|
||||
"Задайте ручной базовый лимит устройств для пользователя. 0 означает безлимит; сброс вернёт тарифный или default-лимит.",
|
||||
effective:
|
||||
"Задайте ручной базовый лимит устройств для пользователя. 0 означает безлимит; сброс вернёт тарифный или default-лимит.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Set a manual base device limit for this user. 0 means unlimited; reset returns to the tariff or default limit.",
|
||||
fallback:
|
||||
"Задайте ручной базовый лимит устройств для пользователя. 0 означает безлимит; сброс вернёт тарифный или default-лимит.",
|
||||
effective:
|
||||
"Set a manual base device limit for this user. 0 means unlimited; reset returns to the tariff or default limit.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_current",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Сейчас: {current}",
|
||||
fallback: "Сейчас: {current}",
|
||||
effective: "Сейчас: {current}",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Current: {current}",
|
||||
fallback: "Сейчас: {current}",
|
||||
effective: "Current: {current}",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_state_default",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "тарифный / default",
|
||||
fallback: "тарифный / default",
|
||||
effective: "тарифный / default",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "tariff / default",
|
||||
fallback: "тарифный / default",
|
||||
effective: "tariff / default",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_state_unlimited",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "безлимит",
|
||||
fallback: "безлимит",
|
||||
effective: "безлимит",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "unlimited",
|
||||
fallback: "безлимит",
|
||||
effective: "unlimited",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_state_count",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "{count} устройств",
|
||||
fallback: "{count} устройств",
|
||||
effective: "{count} устройств",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "{count} devices",
|
||||
fallback: "{count} устройств",
|
||||
effective: "{count} devices",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_state_with_extra",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "{total} устройств ({base} + {extra})",
|
||||
fallback: "{total} устройств ({base} + {extra})",
|
||||
effective: "{total} устройств ({base} + {extra})",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "{total} devices ({base} + {extra})",
|
||||
fallback: "{total} устройств ({base} + {extra})",
|
||||
effective: "{total} devices ({base} + {extra})",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_btn_set_number",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "🔢 Задать число",
|
||||
fallback: "🔢 Задать число",
|
||||
effective: "🔢 Задать число",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "🔢 Set number",
|
||||
fallback: "🔢 Задать число",
|
||||
effective: "🔢 Set number",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_btn_unlimited",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "♾ Включить безлимит",
|
||||
fallback: "♾ Включить безлимит",
|
||||
effective: "♾ Включить безлимит",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "♾ Make unlimited",
|
||||
fallback: "♾ Включить безлимит",
|
||||
effective: "♾ Make unlimited",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_btn_reset",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "↩️ Вернуть тариф/default",
|
||||
fallback: "↩️ Вернуть тариф/default",
|
||||
effective: "↩️ Вернуть тариф/default",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "↩️ Use tariff/default",
|
||||
fallback: "↩️ Вернуть тариф/default",
|
||||
effective: "↩️ Use tariff/default",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_prompt",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Введите лимит HWID-устройств для пользователя {user_id}. 0 означает безлимит:",
|
||||
fallback:
|
||||
"Введите лимит HWID-устройств для пользователя {user_id}. 0 означает безлимит:",
|
||||
effective:
|
||||
"Введите лимит HWID-устройств для пользователя {user_id}. 0 означает безлимит:",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Enter the HWID device limit for user {user_id}. 0 means unlimited:",
|
||||
fallback:
|
||||
"Введите лимит HWID-устройств для пользователя {user_id}. 0 означает безлимит:",
|
||||
effective: "Enter the HWID device limit for user {user_id}. 0 means unlimited:",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_invalid",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "❌ Введите целое число устройств от 0 до 1 000 000.",
|
||||
fallback: "❌ Введите целое число устройств от 0 до 1 000 000.",
|
||||
effective: "❌ Введите целое число устройств от 0 до 1 000 000.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "❌ Enter an integer device count from 0 to 1,000,000.",
|
||||
fallback: "❌ Введите целое число устройств от 0 до 1 000 000.",
|
||||
effective: "❌ Enter an integer device count from 0 to 1,000,000.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_no_subscription",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "У пользователя нет активной подписки",
|
||||
fallback: "У пользователя нет активной подписки",
|
||||
effective: "У пользователя нет активной подписки",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "User has no active subscription",
|
||||
fallback: "У пользователя нет активной подписки",
|
||||
effective: "User has no active subscription",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_state_missing",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "⚠️ Целевой пользователь не найден в состоянии. Откройте карточку пользователя заново.",
|
||||
fallback:
|
||||
"⚠️ Целевой пользователь не найден в состоянии. Откройте карточку пользователя заново.",
|
||||
effective:
|
||||
"⚠️ Целевой пользователь не найден в состоянии. Откройте карточку пользователя заново.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "⚠️ Target user missing in state. Open the user card again.",
|
||||
fallback:
|
||||
"⚠️ Целевой пользователь не найден в состоянии. Откройте карточку пользователя заново.",
|
||||
effective: "⚠️ Target user missing in state. Open the user card again.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_set",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "✅ Лимит HWID-устройств для пользователя {user_id}: {current}",
|
||||
fallback: "✅ Лимит HWID-устройств для пользователя {user_id}: {current}",
|
||||
effective: "✅ Лимит HWID-устройств для пользователя {user_id}: {current}",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "✅ HWID device limit for user {user_id}: {current}",
|
||||
fallback: "✅ Лимит HWID-устройств для пользователя {user_id}: {current}",
|
||||
effective: "✅ HWID device limit for user {user_id}: {current}",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_saved",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "✅ Лимит HWID-устройств сохранён",
|
||||
fallback: "✅ Лимит HWID-устройств сохранён",
|
||||
effective: "✅ Лимит HWID-устройств сохранён",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "✅ HWID device limit saved",
|
||||
fallback: "✅ Лимит HWID-устройств сохранён",
|
||||
effective: "✅ HWID device limit saved",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "admin_hwid_limit_save_error",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "❌ Не удалось сохранить лимит HWID-устройств",
|
||||
fallback: "❌ Не удалось сохранить лимит HWID-устройств",
|
||||
effective: "❌ Не удалось сохранить лимит HWID-устройств",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "❌ Failed to save HWID device limit",
|
||||
fallback: "❌ Не удалось сохранить лимит HWID-устройств",
|
||||
effective: "❌ Failed to save HWID device limit",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_label_hwid_devices",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "HWID-устройства",
|
||||
fallback: "HWID-устройства",
|
||||
effective: "HWID-устройства",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "HWID devices",
|
||||
fallback: "HWID-устройства",
|
||||
effective: "HWID devices",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_card_title",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "HWID-устройства",
|
||||
fallback: "HWID-устройства",
|
||||
effective: "HWID-устройства",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "HWID devices",
|
||||
fallback: "HWID-устройства",
|
||||
effective: "HWID devices",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_card_hint",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Ручной лимит устройств для пользователя. Пустое поле вернёт тарифный или default-лимит.",
|
||||
fallback:
|
||||
"Ручной лимит устройств для пользователя. Пустое поле вернёт тарифный или default-лимит.",
|
||||
effective:
|
||||
"Ручной лимит устройств для пользователя. Пустое поле вернёт тарифный или default-лимит.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Manual device limit for this user. Empty value returns to the tariff or default limit.",
|
||||
fallback:
|
||||
"Ручной лимит устройств для пользователя. Пустое поле вернёт тарифный или default-лимит.",
|
||||
effective:
|
||||
"Manual device limit for this user. Empty value returns to the tariff or default limit.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_input",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Лимит устройств",
|
||||
fallback: "Лимит устройств",
|
||||
effective: "Лимит устройств",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Device limit",
|
||||
fallback: "Лимит устройств",
|
||||
effective: "Device limit",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_input_hint",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Пусто — тариф/default; 0 или галочка — безлимит.",
|
||||
fallback: "Пусто — тариф/default; 0 или галочка — безлимит.",
|
||||
effective: "Пусто — тариф/default; 0 или галочка — безлимит.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Empty means tariff/default; 0 or the checkbox means unlimited.",
|
||||
fallback: "Пусто — тариф/default; 0 или галочка — безлимит.",
|
||||
effective: "Empty means tariff/default; 0 or the checkbox means unlimited.",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_default_placeholder",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Тариф",
|
||||
fallback: "Тариф",
|
||||
effective: "Тариф",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Tariff",
|
||||
fallback: "Тариф",
|
||||
effective: "Tariff",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_save",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Сохранить",
|
||||
fallback: "Сохранить",
|
||||
effective: "Сохранить",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Save",
|
||||
fallback: "Сохранить",
|
||||
effective: "Save",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_status",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Сейчас: {current}",
|
||||
fallback: "Сейчас: {current}",
|
||||
effective: "Сейчас: {current}",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Current: {current}",
|
||||
fallback: "Сейчас: {current}",
|
||||
effective: "Current: {current}",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_default",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Тарифный / default",
|
||||
fallback: "Тарифный / default",
|
||||
effective: "Тарифный / default",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Tariff / default",
|
||||
fallback: "Тарифный / default",
|
||||
effective: "Tariff / default",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_unlimited",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Безлимит",
|
||||
fallback: "Безлимит",
|
||||
effective: "Безлимит",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Unlimited",
|
||||
fallback: "Безлимит",
|
||||
effective: "Unlimited",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_count",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "{count} устройств",
|
||||
fallback: "{count} устройств",
|
||||
effective: "{count} устройств",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "{count} devices",
|
||||
fallback: "{count} устройств",
|
||||
effective: "{count} devices",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "user_hwid_limit_with_extra",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "{total} устройств ({base} + {extra})",
|
||||
fallback: "{total} устройств ({base} + {extra})",
|
||||
effective: "{total} устройств ({base} + {extra})",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "{total} devices ({base} + {extra})",
|
||||
fallback: "{total} устройств ({base} + {extra})",
|
||||
effective: "{total} devices ({base} + {extra})",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "hwid_limit_saved",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Лимит устройств сохранён",
|
||||
fallback: "Лимит устройств сохранён",
|
||||
effective: "Лимит устройств сохранён",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Device limit saved",
|
||||
fallback: "Лимит устройств сохранён",
|
||||
effective: "Device limit saved",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "hwid_limit_invalid",
|
||||
audience: "internal",
|
||||
values: {
|
||||
ru: {
|
||||
base: "Введите целое число устройств от 0 до 1 000 000 или включите безлимит",
|
||||
fallback: "Введите целое число устройств от 0 до 1 000 000 или включите безлимит",
|
||||
effective: "Введите целое число устройств от 0 до 1 000 000 или включите безлимит",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
en: {
|
||||
base: "Enter an integer device count from 0 to 1,000,000 or enable unlimited",
|
||||
fallback: "Введите целое число устройств от 0 до 1 000 000 или включите безлимит",
|
||||
effective: "Enter an integer device count from 0 to 1,000,000 or enable unlimited",
|
||||
override: "",
|
||||
overridden: false,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user