This commit is contained in:
parent
c06a187fd6
commit
627be9a4f1
|
@ -861,6 +861,44 @@ const AdminPage: Component<AdminPageProps> = (props) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Кнопка копирования значения переменной окружения
|
||||
* @param value - значение для копирования
|
||||
*/
|
||||
function CopyButton({ value }: { value: string }) {
|
||||
/**
|
||||
* Копирует значение в буфер обмена
|
||||
* @param e - событие клика
|
||||
*/
|
||||
const handleCopy = async (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
await navigator.clipboard.writeText(value)
|
||||
// Можно добавить всплывающее уведомление
|
||||
} catch (err) {
|
||||
alert('Ошибка копирования: ' + (err as Error).message)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<button class="copy-btn" title="Скопировать" onClick={handleCopy} style="margin-left: 6px">
|
||||
📋
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Кнопка показать/скрыть значение переменной
|
||||
* @param shown - показывать ли значение
|
||||
* @param onToggle - обработчик переключения
|
||||
*/
|
||||
function ShowHideButton({ shown, onToggle }: { shown: boolean, onToggle: () => void }) {
|
||||
return (
|
||||
<button class="show-btn" title={shown ? 'Скрыть' : 'Показать'} onClick={onToggle} style="margin-left: 6px">
|
||||
{shown ? '🙈' : '👁️'}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Компонент модального окна для редактирования переменной окружения
|
||||
*/
|
||||
|
@ -909,6 +947,17 @@ const AdminPage: Component<AdminPageProps> = (props) => {
|
|||
* Компонент для отображения переменных окружения
|
||||
*/
|
||||
const EnvVariablesTab: Component = () => {
|
||||
// Сигналы для показа/скрытия значений по ключу
|
||||
const [shownVars, setShownVars] = createSignal<{ [key: string]: boolean }>({})
|
||||
|
||||
/**
|
||||
* Переключает показ значения переменной
|
||||
* @param key - ключ переменной
|
||||
*/
|
||||
const toggleShow = (key: string) => {
|
||||
setShownVars((prev) => ({ ...prev, [key]: !prev[key] }))
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="env-variables-container">
|
||||
<Show when={envLoading()}>
|
||||
|
@ -928,7 +977,6 @@ const AdminPage: Component<AdminPageProps> = (props) => {
|
|||
<Show when={section.description}>
|
||||
<p class="section-description">{section.description}</p>
|
||||
</Show>
|
||||
|
||||
<div class="variables-list">
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -941,13 +989,19 @@ const AdminPage: Component<AdminPageProps> = (props) => {
|
|||
</thead>
|
||||
<tbody>
|
||||
<For each={section.variables}>
|
||||
{(variable) => (
|
||||
{(variable) => {
|
||||
const shown = shownVars()[variable.key] || false
|
||||
return (
|
||||
<tr>
|
||||
<td>{variable.key}</td>
|
||||
<td>
|
||||
{variable.isSecret
|
||||
{variable.isSecret && !shown
|
||||
? '••••••••'
|
||||
: (variable.value || <span class="empty-value">не задано</span>)}
|
||||
<CopyButton value={variable.value || ''} />
|
||||
{variable.isSecret && (
|
||||
<ShowHideButton shown={shown} onToggle={() => toggleShow(variable.key)} />
|
||||
)}
|
||||
</td>
|
||||
<td>{variable.description || '-'}</td>
|
||||
<td class="actions">
|
||||
|
@ -959,7 +1013,8 @@ const AdminPage: Component<AdminPageProps> = (props) => {
|
|||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -86,7 +86,9 @@ class EnvManager:
|
|||
# Переменные, которые следует всегда помечать как секретные
|
||||
SECRET_VARS_PATTERNS = [
|
||||
r".*TOKEN.*", r".*SECRET.*", r".*PASSWORD.*", r".*KEY.*",
|
||||
r".*PWD.*", r".*PASS.*", r".*CRED.*"
|
||||
r".*PWD.*", r".*PASS.*", r".*CRED.*",
|
||||
r".*JWT.*", r".*SESSION.*", r".*OAUTH.*",
|
||||
r".*GITHUB.*", r".*GOOGLE.*", r".*FACEBOOK.*"
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
@ -178,9 +180,27 @@ class EnvManager:
|
|||
|
||||
def _is_secret_variable(self, key: str) -> bool:
|
||||
"""
|
||||
Проверяет, является ли переменная секретной
|
||||
Проверяет, является ли переменная секретной.
|
||||
Секретными считаются:
|
||||
- переменные, подходящие под SECRET_VARS_PATTERNS
|
||||
- переменные с ключами DATABASE_URL, REDIS_URL, DB_URL (точное совпадение, без учета регистра)
|
||||
|
||||
>>> EnvManager()._is_secret_variable('MY_SECRET_TOKEN')
|
||||
True
|
||||
>>> EnvManager()._is_secret_variable('database_url')
|
||||
True
|
||||
>>> EnvManager()._is_secret_variable('REDIS_URL')
|
||||
True
|
||||
>>> EnvManager()._is_secret_variable('DB_URL')
|
||||
True
|
||||
>>> EnvManager()._is_secret_variable('SOME_PUBLIC_KEY')
|
||||
True
|
||||
>>> EnvManager()._is_secret_variable('SOME_PUBLIC_VAR')
|
||||
False
|
||||
"""
|
||||
key_upper = key.upper()
|
||||
if key_upper in {"DATABASE_URL", "REDIS_URL", "DB_URL"}:
|
||||
return True
|
||||
return any(re.match(pattern, key_upper) for pattern in self.SECRET_VARS_PATTERNS)
|
||||
|
||||
def _determine_variable_type(self, value: str) -> str:
|
||||
|
|
|
@ -75,7 +75,7 @@ class ViewedStorage:
|
|||
await redis.connect()
|
||||
|
||||
# Логируем настройки Redis соединения
|
||||
logger.info(f" * Redis connection: {redis._client}")
|
||||
logger.info("* Redis connected")
|
||||
|
||||
# Получаем список всех ключей migrated_views_* и находим самый последний
|
||||
keys = await redis.execute("KEYS", "migrated_views_*")
|
||||
|
|
Loading…
Reference in New Issue
Block a user