Remove auth profile references from secrets page
This commit is contained in:
@@ -19,26 +19,6 @@
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
line-height: 1.55;
|
line-height: 1.55;
|
||||||
}
|
}
|
||||||
.secret-ref-list {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
.secret-ref-pill {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
padding: 5px 8px;
|
|
||||||
border-radius: 999px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
background: var(--bg-canvas);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 11.5px;
|
|
||||||
}
|
|
||||||
.secret-ref-pill strong {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.secrets-card-list { display: none; }
|
.secrets-card-list { display: none; }
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
#secrets-table-wrap { display: none; }
|
#secrets-table-wrap { display: none; }
|
||||||
@@ -100,7 +80,7 @@
|
|||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="page-header-text">
|
<div class="page-header-text">
|
||||||
<h1 class="page-title" data-i18n="secrets.title">Secrets</h1>
|
<h1 class="page-title" data-i18n="secrets.title">Secrets</h1>
|
||||||
<p class="page-subtitle" data-i18n="secrets.subtitle">Encrypted upstream credentials for bearer tokens, basic auth, and API keys used by auth profiles.</p>
|
<p class="page-subtitle" data-i18n="secrets.subtitle">Encrypted credentials for upstream REST APIs.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-header-actions">
|
<div class="page-header-actions">
|
||||||
<button class="btn-primary" id="btn-create-secret" data-testid="secret-create-button" type="button">
|
<button class="btn-primary" id="btn-create-secret" data-testid="secret-create-button" type="button">
|
||||||
@@ -152,18 +132,6 @@
|
|||||||
<div class="resource-list secrets-card-list" id="secrets-card-list"></div>
|
<div class="resource-list secrets-card-list" id="secrets-card-list"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section-card" style="margin-top: 20px;">
|
|
||||||
<div class="section-card-header">
|
|
||||||
<div>
|
|
||||||
<div class="section-card-title" data-i18n="secrets.profiles.title">Auth profile references</div>
|
|
||||||
<div class="section-card-subtitle" id="secret-profiles-summary" data-i18n="secrets.profiles.subtitle">Profiles resolve secrets at runtime before upstream execution.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section-card-body">
|
|
||||||
<div class="resource-list" id="auth-profiles-list"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-overlay" id="secret-modal" data-testid="secret-modal">
|
<div class="modal-overlay" id="secret-modal" data-testid="secret-modal">
|
||||||
|
|||||||
+14
-62
@@ -174,7 +174,7 @@ var TRANSLATIONS = {
|
|||||||
|
|
||||||
// Secrets page
|
// Secrets page
|
||||||
'secrets.title': 'Secrets',
|
'secrets.title': 'Secrets',
|
||||||
'secrets.subtitle': 'Encrypted upstream credentials for bearer tokens, basic auth, and API keys used by auth profiles.',
|
'secrets.subtitle': 'Encrypted credentials for upstream REST APIs.',
|
||||||
'secrets.new': 'Create secret',
|
'secrets.new': 'Create secret',
|
||||||
'secrets.callout.title': 'Secret plaintext is write-only.',
|
'secrets.callout.title': 'Secret plaintext is write-only.',
|
||||||
'secrets.callout.body': 'Crank encrypts secret values with the workspace master key. After creation or rotation, the API returns only metadata, so this page focuses on secret management and where each secret is used.',
|
'secrets.callout.body': 'Crank encrypts secret values with the workspace master key. After creation or rotation, the API returns only metadata, so this page focuses on secret management and where each secret is used.',
|
||||||
@@ -194,8 +194,8 @@ var TRANSLATIONS = {
|
|||||||
'secrets.status.active': 'Active',
|
'secrets.status.active': 'Active',
|
||||||
'secrets.status.disabled': 'Disabled',
|
'secrets.status.disabled': 'Disabled',
|
||||||
'secrets.last_used.never': 'Never',
|
'secrets.last_used.never': 'Never',
|
||||||
'secrets.used_by_count.one': '{count} profile',
|
'secrets.used_by_count.one': '{count} binding',
|
||||||
'secrets.used_by_count.other': '{count} profiles',
|
'secrets.used_by_count.other': '{count} bindings',
|
||||||
'secrets.loading': 'Loading…',
|
'secrets.loading': 'Loading…',
|
||||||
'secrets.empty.none': 'No secrets yet',
|
'secrets.empty.none': 'No secrets yet',
|
||||||
'secrets.empty.search': 'No secrets match your search',
|
'secrets.empty.search': 'No secrets match your search',
|
||||||
@@ -203,7 +203,7 @@ var TRANSLATIONS = {
|
|||||||
'secrets.error.load': 'Failed to load secrets',
|
'secrets.error.load': 'Failed to load secrets',
|
||||||
'secrets.action.rotate': 'Rotate',
|
'secrets.action.rotate': 'Rotate',
|
||||||
'secrets.action.delete': 'Delete',
|
'secrets.action.delete': 'Delete',
|
||||||
'secrets.confirm.delete': 'Delete secret {name}? This cannot be undone while the secret is still referenced by an auth profile.',
|
'secrets.confirm.delete': 'Delete secret {name}? This cannot be undone while the secret is still used by an operation.',
|
||||||
'secrets.toast.create_title': 'Secret created',
|
'secrets.toast.create_title': 'Secret created',
|
||||||
'secrets.toast.create_message': 'The secret was stored encrypted. Plaintext cannot be viewed again through the API.',
|
'secrets.toast.create_message': 'The secret was stored encrypted. Plaintext cannot be viewed again through the API.',
|
||||||
'secrets.toast.create_error_title': 'Secret creation failed',
|
'secrets.toast.create_error_title': 'Secret creation failed',
|
||||||
@@ -236,35 +236,12 @@ var TRANSLATIONS = {
|
|||||||
'secrets.modal.rotating': 'Rotating…',
|
'secrets.modal.rotating': 'Rotating…',
|
||||||
'secrets.hint.token': 'Best for bearer tokens and simple API key values.',
|
'secrets.hint.token': 'Best for bearer tokens and simple API key values.',
|
||||||
'secrets.hint.username_password': 'Stores username and password as one encrypted payload.',
|
'secrets.hint.username_password': 'Stores username and password as one encrypted payload.',
|
||||||
'secrets.hint.header': 'Stores a raw header value. Header name is configured in the auth profile.',
|
'secrets.hint.header': 'Stores a raw header value. Header name is configured in the operation wizard.',
|
||||||
'secrets.hint.generic': 'Stores arbitrary JSON for custom integrations that need a structured secret payload.',
|
'secrets.hint.generic': 'Stores arbitrary JSON for custom integrations that need a structured secret payload.',
|
||||||
'secrets.validation.name_required': 'Secret name is required.',
|
'secrets.validation.name_required': 'Secret name is required.',
|
||||||
'secrets.validation.value_required': 'Secret value is required.',
|
'secrets.validation.value_required': 'Secret value is required.',
|
||||||
'secrets.validation.username_password_required': 'Username and password are both required.',
|
'secrets.validation.username_password_required': 'Username and password are both required.',
|
||||||
'secrets.validation.json_invalid': 'Generic secret payload must be valid JSON.',
|
'secrets.validation.json_invalid': 'Generic secret payload must be valid JSON.',
|
||||||
'secrets.profiles.title': 'Auth profile references',
|
|
||||||
'secrets.profiles.subtitle': 'Profiles resolve secrets at runtime before upstream execution.',
|
|
||||||
'secrets.profiles.subtitle_count.one': '{count} auth profile',
|
|
||||||
'secrets.profiles.subtitle_count.other': '{count} auth profiles',
|
|
||||||
'secrets.profiles.error_title': 'Unable to load auth profile references',
|
|
||||||
'secrets.profiles.loading_title': 'Loading auth profile references…',
|
|
||||||
'secrets.profiles.loading_body': 'Fetching auth profiles for the current workspace.',
|
|
||||||
'secrets.profiles.empty_title': 'No auth profiles yet',
|
|
||||||
'secrets.profiles.empty_body': 'Create auth profiles in the Wizard when configuring an operation.',
|
|
||||||
'secrets.profiles.references': 'Referenced secrets',
|
|
||||||
'secrets.profiles.reference_count': '{count} refs',
|
|
||||||
'secrets.profiles.meta.created': 'Created',
|
|
||||||
'secrets.profiles.meta.updated': 'Updated',
|
|
||||||
'secrets.profiles.summary_bearer': 'Bearer auth via {header} using {secret}.',
|
|
||||||
'secrets.profiles.summary_basic': 'Basic auth using {username} and {password}.',
|
|
||||||
'secrets.profiles.summary_api_key_header': 'API key header {header} using {secret}.',
|
|
||||||
'secrets.profiles.summary_api_key_query': 'API key query param {param} using {secret}.',
|
|
||||||
'secrets.profiles.summary_unknown': 'Unknown auth profile configuration.',
|
|
||||||
'secrets.auth_kind.bearer': 'Bearer auth',
|
|
||||||
'secrets.auth_kind.basic': 'Basic auth',
|
|
||||||
'secrets.auth_kind.api_key_header': 'API key header',
|
|
||||||
'secrets.auth_kind.api_key_query': 'API key query',
|
|
||||||
|
|
||||||
// Logs page
|
// Logs page
|
||||||
'logs.title': 'Logs',
|
'logs.title': 'Logs',
|
||||||
'logs.subtitle': 'Real-time invocation log for all operations in this workspace.',
|
'logs.subtitle': 'Real-time invocation log for all operations in this workspace.',
|
||||||
@@ -566,7 +543,7 @@ var TRANSLATIONS = {
|
|||||||
'wizard.step2.auth_mode.create': 'Create auth profile now',
|
'wizard.step2.auth_mode.create': 'Create auth profile now',
|
||||||
'wizard.step2.auth_profile': 'Auth profile',
|
'wizard.step2.auth_profile': 'Auth profile',
|
||||||
'wizard.step2.auth_profile_hint': 'The selected auth profile is applied automatically when the tool runs.',
|
'wizard.step2.auth_profile_hint': 'The selected auth profile is applied automatically when the tool runs.',
|
||||||
'wizard.step2.auth_profile_empty': 'No auth profiles yet. Create one below or on the Secrets page.',
|
'wizard.step2.auth_profile_empty': 'No auth profiles yet. Create one below.',
|
||||||
'wizard.step2.create_profile_title': 'Create auth profile',
|
'wizard.step2.create_profile_title': 'Create auth profile',
|
||||||
'wizard.step2.create_profile_subtitle': 'Create a reusable profile backed by encrypted workspace secrets.',
|
'wizard.step2.create_profile_subtitle': 'Create a reusable profile backed by encrypted workspace secrets.',
|
||||||
'wizard.step2.profile_name': 'Profile name',
|
'wizard.step2.profile_name': 'Profile name',
|
||||||
@@ -1082,7 +1059,7 @@ var TRANSLATIONS = {
|
|||||||
|
|
||||||
// Secrets page
|
// Secrets page
|
||||||
'secrets.title': 'Секреты',
|
'secrets.title': 'Секреты',
|
||||||
'secrets.subtitle': 'Зашифрованные upstream-учетные данные для bearer token, basic auth и API key, которые используют auth profiles.',
|
'secrets.subtitle': 'Зашифрованные учетные данные для upstream REST API.',
|
||||||
'secrets.new': 'Создать секрет',
|
'secrets.new': 'Создать секрет',
|
||||||
'secrets.callout.title': 'Plaintext секрета доступен только на запись.',
|
'secrets.callout.title': 'Plaintext секрета доступен только на запись.',
|
||||||
'secrets.callout.body': 'Crank шифрует значения секретов мастер-ключом воркспейса. После создания или ротации API возвращает только метаданные, поэтому на этой странице доступны управление секретами и просмотр их использования.',
|
'secrets.callout.body': 'Crank шифрует значения секретов мастер-ключом воркспейса. После создания или ротации API возвращает только метаданные, поэтому на этой странице доступны управление секретами и просмотр их использования.',
|
||||||
@@ -1102,10 +1079,10 @@ var TRANSLATIONS = {
|
|||||||
'secrets.status.active': 'Активен',
|
'secrets.status.active': 'Активен',
|
||||||
'secrets.status.disabled': 'Отключен',
|
'secrets.status.disabled': 'Отключен',
|
||||||
'secrets.last_used.never': 'Никогда',
|
'secrets.last_used.never': 'Никогда',
|
||||||
'secrets.used_by_count.one': '{count} профиль',
|
'secrets.used_by_count.one': '{count} привязка',
|
||||||
'secrets.used_by_count.few': '{count} профиля',
|
'secrets.used_by_count.few': '{count} привязки',
|
||||||
'secrets.used_by_count.many': '{count} профилей',
|
'secrets.used_by_count.many': '{count} привязок',
|
||||||
'secrets.used_by_count.other': '{count} профиля',
|
'secrets.used_by_count.other': '{count} привязки',
|
||||||
'secrets.loading': 'Загрузка…',
|
'secrets.loading': 'Загрузка…',
|
||||||
'secrets.empty.none': 'Секретов пока нет',
|
'secrets.empty.none': 'Секретов пока нет',
|
||||||
'secrets.empty.search': 'Нет секретов по текущему поиску',
|
'secrets.empty.search': 'Нет секретов по текущему поиску',
|
||||||
@@ -1113,7 +1090,7 @@ var TRANSLATIONS = {
|
|||||||
'secrets.error.load': 'Не удалось загрузить секреты',
|
'secrets.error.load': 'Не удалось загрузить секреты',
|
||||||
'secrets.action.rotate': 'Ротировать',
|
'secrets.action.rotate': 'Ротировать',
|
||||||
'secrets.action.delete': 'Удалить',
|
'secrets.action.delete': 'Удалить',
|
||||||
'secrets.confirm.delete': 'Удалить секрет {name}? Это нельзя сделать, пока на секрет ссылается auth profile.',
|
'secrets.confirm.delete': 'Удалить секрет {name}? Это нельзя сделать, пока секрет используется в операции.',
|
||||||
'secrets.toast.create_title': 'Секрет создан',
|
'secrets.toast.create_title': 'Секрет создан',
|
||||||
'secrets.toast.create_message': 'Секрет сохранен в зашифрованном виде. Повторно посмотреть plaintext через API нельзя.',
|
'secrets.toast.create_message': 'Секрет сохранен в зашифрованном виде. Повторно посмотреть plaintext через API нельзя.',
|
||||||
'secrets.toast.create_error_title': 'Не удалось создать секрет',
|
'secrets.toast.create_error_title': 'Не удалось создать секрет',
|
||||||
@@ -1146,37 +1123,12 @@ var TRANSLATIONS = {
|
|||||||
'secrets.modal.rotating': 'Ротация…',
|
'secrets.modal.rotating': 'Ротация…',
|
||||||
'secrets.hint.token': 'Лучший вариант для bearer tokens и простых API key values.',
|
'secrets.hint.token': 'Лучший вариант для bearer tokens и простых API key values.',
|
||||||
'secrets.hint.username_password': 'Хранит логин и пароль как один зашифрованный payload.',
|
'secrets.hint.username_password': 'Хранит логин и пароль как один зашифрованный payload.',
|
||||||
'secrets.hint.header': 'Хранит сырое значение заголовка. Имя заголовка задается в auth profile.',
|
'secrets.hint.header': 'Хранит сырое значение заголовка. Имя заголовка задается в мастере операции.',
|
||||||
'secrets.hint.generic': 'Хранит произвольный JSON для нестандартных интеграций, которым нужен структурированный секрет.',
|
'secrets.hint.generic': 'Хранит произвольный JSON для нестандартных интеграций, которым нужен структурированный секрет.',
|
||||||
'secrets.validation.name_required': 'Нужно указать имя секрета.',
|
'secrets.validation.name_required': 'Нужно указать имя секрета.',
|
||||||
'secrets.validation.value_required': 'Нужно указать значение секрета.',
|
'secrets.validation.value_required': 'Нужно указать значение секрета.',
|
||||||
'secrets.validation.username_password_required': 'Нужно указать и логин, и пароль.',
|
'secrets.validation.username_password_required': 'Нужно указать и логин, и пароль.',
|
||||||
'secrets.validation.json_invalid': 'Generic secret payload должен быть валидным JSON.',
|
'secrets.validation.json_invalid': 'Generic secret payload должен быть валидным JSON.',
|
||||||
'secrets.profiles.title': 'Ссылки auth profiles',
|
|
||||||
'secrets.profiles.subtitle': 'Профили резолвят секреты в runtime перед upstream execution.',
|
|
||||||
'secrets.profiles.subtitle_count.one': '{count} профиль авторизации',
|
|
||||||
'secrets.profiles.subtitle_count.few': '{count} профиля авторизации',
|
|
||||||
'secrets.profiles.subtitle_count.many': '{count} профилей авторизации',
|
|
||||||
'secrets.profiles.subtitle_count.other': '{count} профиля авторизации',
|
|
||||||
'secrets.profiles.error_title': 'Не удалось загрузить ссылки auth profiles',
|
|
||||||
'secrets.profiles.loading_title': 'Загрузка ссылок auth profiles…',
|
|
||||||
'secrets.profiles.loading_body': 'Получаем auth profiles для текущего воркспейса.',
|
|
||||||
'secrets.profiles.empty_title': 'Auth profiles пока нет',
|
|
||||||
'secrets.profiles.empty_body': 'Создайте auth profile в мастере при настройке операции.',
|
|
||||||
'secrets.profiles.references': 'Связанные секреты',
|
|
||||||
'secrets.profiles.reference_count': '{count} ссылок',
|
|
||||||
'secrets.profiles.meta.created': 'Создан',
|
|
||||||
'secrets.profiles.meta.updated': 'Обновлен',
|
|
||||||
'secrets.profiles.summary_bearer': 'Bearer auth через {header} с использованием {secret}.',
|
|
||||||
'secrets.profiles.summary_basic': 'Basic auth с использованием {username} и {password}.',
|
|
||||||
'secrets.profiles.summary_api_key_header': 'API key header {header} с использованием {secret}.',
|
|
||||||
'secrets.profiles.summary_api_key_query': 'API key query param {param} с использованием {secret}.',
|
|
||||||
'secrets.profiles.summary_unknown': 'Неизвестная конфигурация auth profile.',
|
|
||||||
'secrets.auth_kind.bearer': 'Bearer auth',
|
|
||||||
'secrets.auth_kind.basic': 'Basic auth',
|
|
||||||
'secrets.auth_kind.api_key_header': 'API key header',
|
|
||||||
'secrets.auth_kind.api_key_query': 'API key query',
|
|
||||||
|
|
||||||
// Logs page
|
// Logs page
|
||||||
'logs.title': 'Логи',
|
'logs.title': 'Логи',
|
||||||
'logs.subtitle': 'Живой журнал вызовов по всем операциям этого воркспейса.',
|
'logs.subtitle': 'Живой журнал вызовов по всем операциям этого воркспейса.',
|
||||||
@@ -1478,7 +1430,7 @@ var TRANSLATIONS = {
|
|||||||
'wizard.step2.auth_mode.create': 'Создать auth profile сейчас',
|
'wizard.step2.auth_mode.create': 'Создать auth profile сейчас',
|
||||||
'wizard.step2.auth_profile': 'Auth profile',
|
'wizard.step2.auth_profile': 'Auth profile',
|
||||||
'wizard.step2.auth_profile_hint': 'Выбранный профиль применяется автоматически при каждом вызове инструмента.',
|
'wizard.step2.auth_profile_hint': 'Выбранный профиль применяется автоматически при каждом вызове инструмента.',
|
||||||
'wizard.step2.auth_profile_empty': 'Auth profiles пока нет. Создайте его ниже или на странице Secrets.',
|
'wizard.step2.auth_profile_empty': 'Auth profiles пока нет. Создайте его ниже.',
|
||||||
'wizard.step2.create_profile_title': 'Создать auth profile',
|
'wizard.step2.create_profile_title': 'Создать auth profile',
|
||||||
'wizard.step2.create_profile_subtitle': 'Создайте переиспользуемый профиль на базе зашифрованных workspace secrets.',
|
'wizard.step2.create_profile_subtitle': 'Создайте переиспользуемый профиль на базе зашифрованных workspace secrets.',
|
||||||
'wizard.step2.profile_name': 'Имя профиля',
|
'wizard.step2.profile_name': 'Имя профиля',
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ function initSecretsPage() {
|
|||||||
var modal = document.getElementById('secret-modal');
|
var modal = document.getElementById('secret-modal');
|
||||||
var tbody = document.getElementById('secrets-tbody');
|
var tbody = document.getElementById('secrets-tbody');
|
||||||
var cardList = document.getElementById('secrets-card-list');
|
var cardList = document.getElementById('secrets-card-list');
|
||||||
var profilesList = document.getElementById('auth-profiles-list');
|
|
||||||
var summary = document.getElementById('secrets-summary');
|
var summary = document.getElementById('secrets-summary');
|
||||||
var profilesSummary = document.getElementById('secret-profiles-summary');
|
|
||||||
var searchInput = document.getElementById('secret-search');
|
var searchInput = document.getElementById('secret-search');
|
||||||
var modalTitle = document.getElementById('secret-modal-title');
|
var modalTitle = document.getElementById('secret-modal-title');
|
||||||
var modalName = document.getElementById('secret-name');
|
var modalName = document.getElementById('secret-name');
|
||||||
@@ -55,10 +53,6 @@ function initSecretsPage() {
|
|||||||
return tKey('secrets.kind.' + String(kind || '').toLowerCase());
|
return tKey('secrets.kind.' + String(kind || '').toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
function authKindLabel(kind) {
|
|
||||||
return tKey('secrets.auth_kind.' + String(kind || '').toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(value) {
|
function formatDate(value) {
|
||||||
if (!value) return '—';
|
if (!value) return '—';
|
||||||
return new Date(value).toLocaleDateString();
|
return new Date(value).toLocaleDateString();
|
||||||
@@ -73,40 +67,6 @@ function initSecretsPage() {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function profileSummary(profile, secretsById) {
|
|
||||||
var config = profile && profile.config ? profile.config : {};
|
|
||||||
if (config.bearer) {
|
|
||||||
return tfKey('secrets.profiles.summary_bearer', {
|
|
||||||
header: config.bearer.header_name,
|
|
||||||
secret: secretName(secretsById, config.bearer.secret_id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (config.basic) {
|
|
||||||
return tfKey('secrets.profiles.summary_basic', {
|
|
||||||
username: secretName(secretsById, config.basic.username_secret_id),
|
|
||||||
password: secretName(secretsById, config.basic.password_secret_id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (config.api_key_header) {
|
|
||||||
return tfKey('secrets.profiles.summary_api_key_header', {
|
|
||||||
header: config.api_key_header.header_name,
|
|
||||||
secret: secretName(secretsById, config.api_key_header.secret_id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (config.api_key_query) {
|
|
||||||
return tfKey('secrets.profiles.summary_api_key_query', {
|
|
||||||
param: config.api_key_query.param_name,
|
|
||||||
secret: secretName(secretsById, config.api_key_query.secret_id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return tKey('secrets.profiles.summary_unknown');
|
|
||||||
}
|
|
||||||
|
|
||||||
function secretName(secretsById, secretId) {
|
|
||||||
var secret = secretsById[secretId];
|
|
||||||
return secret ? secret.name : secretId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildUsageBySecret() {
|
function buildUsageBySecret() {
|
||||||
var usage = {};
|
var usage = {};
|
||||||
state.profiles.forEach(function (profile) {
|
state.profiles.forEach(function (profile) {
|
||||||
@@ -451,125 +411,11 @@ function initSecretsPage() {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderProfiles() {
|
|
||||||
var usage = state.derived.usageBySecret;
|
|
||||||
var secretsById = state.derived.secretsById;
|
|
||||||
|
|
||||||
profilesSummary.textContent = window.tPlural(
|
|
||||||
'secrets.profiles.subtitle_count',
|
|
||||||
state.profiles.length,
|
|
||||||
{ count: state.profiles.length }
|
|
||||||
);
|
|
||||||
|
|
||||||
window.CrankDom.clear(profilesList);
|
|
||||||
|
|
||||||
if (state.error) {
|
|
||||||
profilesList.appendChild(window.CrankDom.createEmptyState(
|
|
||||||
tKey('secrets.profiles.error_title'),
|
|
||||||
state.error
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.loading && state.profiles.length === 0) {
|
|
||||||
profilesList.appendChild(window.CrankDom.createEmptyState(
|
|
||||||
tKey('secrets.profiles.loading_title'),
|
|
||||||
tKey('secrets.profiles.loading_body')
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state.profiles.length) {
|
|
||||||
profilesList.appendChild(window.CrankDom.createEmptyState(
|
|
||||||
tKey('secrets.profiles.empty_title'),
|
|
||||||
tKey('secrets.profiles.empty_body')
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.profiles.forEach(function (profile) {
|
|
||||||
var secretIds = secretIdsForProfile(profile);
|
|
||||||
var card = document.createElement('div');
|
|
||||||
card.className = 'resource-card';
|
|
||||||
|
|
||||||
var header = document.createElement('div');
|
|
||||||
header.className = 'resource-card-header';
|
|
||||||
var headerBody = document.createElement('div');
|
|
||||||
|
|
||||||
var title = document.createElement('div');
|
|
||||||
title.className = 'resource-card-title';
|
|
||||||
title.textContent = profile.name;
|
|
||||||
headerBody.appendChild(title);
|
|
||||||
|
|
||||||
var subtitle = document.createElement('div');
|
|
||||||
subtitle.className = 'resource-card-subtitle';
|
|
||||||
subtitle.textContent = profileSummary(profile, secretsById);
|
|
||||||
headerBody.appendChild(subtitle);
|
|
||||||
|
|
||||||
var pillRow = document.createElement('div');
|
|
||||||
pillRow.className = 'resource-pill-row';
|
|
||||||
var statusPill = document.createElement('span');
|
|
||||||
statusPill.className = 'resource-status-pill active';
|
|
||||||
statusPill.textContent = authKindLabel(profile.kind);
|
|
||||||
pillRow.appendChild(statusPill);
|
|
||||||
headerBody.appendChild(pillRow);
|
|
||||||
header.appendChild(headerBody);
|
|
||||||
card.appendChild(header);
|
|
||||||
|
|
||||||
var metaGrid = document.createElement('div');
|
|
||||||
metaGrid.className = 'resource-meta-grid';
|
|
||||||
[
|
|
||||||
[tKey('secrets.profiles.meta.created'), formatDate(profile.created_at)],
|
|
||||||
[tKey('secrets.profiles.meta.updated'), formatDate(profile.updated_at)]
|
|
||||||
].forEach(function(entry) {
|
|
||||||
var item = document.createElement('div');
|
|
||||||
item.className = 'resource-meta-item';
|
|
||||||
var label = document.createElement('div');
|
|
||||||
label.className = 'resource-meta-label';
|
|
||||||
label.textContent = entry[0];
|
|
||||||
var value = document.createElement('div');
|
|
||||||
value.className = 'resource-meta-value';
|
|
||||||
value.textContent = entry[1];
|
|
||||||
item.appendChild(label);
|
|
||||||
item.appendChild(value);
|
|
||||||
metaGrid.appendChild(item);
|
|
||||||
});
|
|
||||||
card.appendChild(metaGrid);
|
|
||||||
|
|
||||||
var detail = document.createElement('div');
|
|
||||||
detail.className = 'resource-detail-block';
|
|
||||||
var detailTitle = document.createElement('div');
|
|
||||||
detailTitle.className = 'resource-detail-title';
|
|
||||||
detailTitle.textContent = tKey('secrets.profiles.references');
|
|
||||||
detail.appendChild(detailTitle);
|
|
||||||
|
|
||||||
var refList = document.createElement('div');
|
|
||||||
refList.className = 'secret-ref-list';
|
|
||||||
secretIds.forEach(function(secretId) {
|
|
||||||
var users = usage[secretId] || [];
|
|
||||||
var pill = document.createElement('span');
|
|
||||||
pill.className = 'secret-ref-pill';
|
|
||||||
var strong = document.createElement('strong');
|
|
||||||
strong.textContent = secretName(secretsById, secretId);
|
|
||||||
var count = document.createElement('span');
|
|
||||||
count.textContent = tfKey('secrets.profiles.reference_count', { count: users.length });
|
|
||||||
pill.appendChild(strong);
|
|
||||||
pill.appendChild(count);
|
|
||||||
refList.appendChild(pill);
|
|
||||||
});
|
|
||||||
detail.appendChild(refList);
|
|
||||||
card.appendChild(detail);
|
|
||||||
|
|
||||||
profilesList.appendChild(card);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
state.workspaceId = currentWorkspaceId();
|
state.workspaceId = currentWorkspaceId();
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
state.error = '';
|
state.error = '';
|
||||||
renderSecrets();
|
renderSecrets();
|
||||||
renderProfiles();
|
|
||||||
|
|
||||||
if (!state.workspaceId) {
|
if (!state.workspaceId) {
|
||||||
state.secrets = [];
|
state.secrets = [];
|
||||||
@@ -578,7 +424,6 @@ function initSecretsPage() {
|
|||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = tKey('secrets.error.workspace');
|
state.error = tKey('secrets.error.workspace');
|
||||||
renderSecrets();
|
renderSecrets();
|
||||||
renderProfiles();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,7 +443,6 @@ function initSecretsPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
renderSecrets();
|
renderSecrets();
|
||||||
renderProfiles();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user