Build and Publish / Build and Publish Docker Image (push) Successful in 1m26s
271 lines
15 KiB
HTML
271 lines
15 KiB
HTML
{% extends "admin/layout.html" %}
|
|
{% let active_page = "settings" %}
|
|
|
|
{% block title %}{{ t.settings_title }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-head">
|
|
<h1>{{ t.settings_title }}</h1>
|
|
</div>
|
|
|
|
{% if saved %}
|
|
<div class="notification is-success is-light">{{ t.settings_saved }}</div>
|
|
{% endif %}
|
|
{% if let Some(message) = error %}
|
|
<div class="notification is-danger is-light">{{ message }}</div>
|
|
{% endif %}
|
|
|
|
<div class="form-card">
|
|
<form method="post" action="/admin/settings/save">
|
|
|
|
<h2 class="subtitle is-5 mb-3" style="border-bottom:1px solid #eee;padding-bottom:0.5rem;">{{ t.settings_contact_info }}</h2>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_contact_info }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="contact_info" placeholder="+7 999 123-45-67 / info@example.com" value="{% for s in &settings %}{% if s.key == "contact_info" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_pricing_info }}</label>
|
|
<div class="control">
|
|
<textarea class="input" name="pricing_info" rows="3" style="min-height:70px;resize:vertical;" placeholder="от 600 рублей за визит">{% for s in &settings %}{% if s.key == "pricing_info" %}{{ s.value }}{% endif %}{% endfor %}</textarea>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_seo_keywords }}</label>
|
|
<div class="control">
|
|
<textarea id="seoKeywordsInput" class="textarea" name="seo_keywords" rows="3"
|
|
style="resize:vertical;"
|
|
placeholder="зооняня Хабаровск, присмотр за питомцем Хабаровск, догситтер Хабаровск">{% for s in &settings %}{% if s.key == "seo_keywords" %}{{ s.value }}{% endif %}{% endfor %}</textarea>
|
|
</div>
|
|
<div id="seoPreview" style="margin-top:0.5rem;padding:0.5rem 0.75rem;background:#fafafa;border:1px solid #eee;border-radius:6px;min-height:2rem;line-height:2;font-size:0.85rem;display:none;"></div>
|
|
</div>
|
|
|
|
<details style="margin-top:1.5rem;">
|
|
<summary class="subtitle is-5 mb-3" style="cursor:pointer;border-bottom:1px solid #eee;padding-bottom:0.5rem;">
|
|
{{ t.settings_section_advanced }}
|
|
</summary>
|
|
|
|
<div style="margin-top:1rem;">
|
|
<h3 class="subtitle is-6 mb-2 has-text-grey">{{ t.settings_section_general }}</h3>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_site_domain }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="site_domain" placeholder="https://example.com" value="{% for s in &settings %}{% if s.key == "site_domain" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_timezone }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="timezone" placeholder="Asia/Vladivostok" value="{% for s in &settings %}{% if s.key == "timezone" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="subtitle is-6 mb-2 has-text-grey" style="margin-top:1.25rem;">{{ t.settings_section_storage }}</h3>
|
|
<div class="notification is-info is-light" style="padding:0.8rem 1rem;font-size:0.85rem;margin-bottom:0.85rem;">
|
|
<p>{{ t.settings_r2_help }}</p>
|
|
<p style="margin-top:0.45rem;">{{ t.settings_r2_migration_help }}</p>
|
|
</div>
|
|
<div class="field">
|
|
<label class="checkbox">
|
|
<input type="checkbox" name="r2_enabled" value="true"{% if r2_enabled_checked %} checked{% endif %}>
|
|
{{ t.settings_r2_enabled }}
|
|
</label>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_r2_account_id }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="r2_account_id" maxlength="32" autocomplete="off" placeholder="0123456789abcdef0123456789abcdef" value="{% for s in &settings %}{% if s.key == "r2_account_id" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_r2_bucket }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="r2_bucket" autocomplete="off" placeholder="pet-media" value="{% for s in &settings %}{% if s.key == "r2_bucket" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_r2_access_key_id }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="r2_access_key_id" autocomplete="off" value="{% for s in &settings %}{% if s.key == "r2_access_key_id" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_r2_secret_access_key }}</label>
|
|
<div class="control">
|
|
<input class="input" type="password" name="r2_secret_access_key" autocomplete="new-password"{% if r2_secret_configured %} placeholder="{{ t.settings_r2_secret_unchanged }}"{% endif %}>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="subtitle is-6 mb-2 has-text-grey" style="margin-top:1.25rem;">{{ t.settings_section_notifications }}</h3>
|
|
<div class="field">
|
|
<label class="checkbox">
|
|
<input type="checkbox" name="client_notifications_enabled" value="true"{% if client_notifications_checked %} checked{% endif %}>
|
|
{{ t.settings_client_notifications_enabled }}
|
|
</label>
|
|
<p class="help">{{ t.settings_client_notifications_help }}</p>
|
|
</div>
|
|
<blockquote class="notification is-warning is-light" style="padding:0.8rem 1rem;font-size:0.85rem;margin:0.75rem 0;">
|
|
<p>{{ t.settings_vapid_warning }}</p>
|
|
<p style="margin-top:0.45rem;">{{ t.settings_vapid_generate }}</p>
|
|
<code style="display:inline-block;margin-top:0.2rem;user-select:all;">cargo run --bin generate_vapid</code>
|
|
</blockquote>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_vapid_public_key }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="vapid_public_key" autocomplete="off" value="{% for s in &settings %}{% if s.key == "vapid_public_key" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_vapid_private_key }}</label>
|
|
<div class="control">
|
|
<input class="input" type="password" name="vapid_private_key" autocomplete="new-password" value="{% for s in &settings %}{% if s.key == "vapid_private_key" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_vapid_subject }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="vapid_subject" placeholder="mailto:admin@example.com" value="{% for s in &settings %}{% if s.key == "vapid_subject" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<details style="margin:0.9rem 0 1.25rem;border:1px solid #e8e5f5;border-radius:8px;background:#faf9ff;">
|
|
<summary style="cursor:pointer;padding:0.7rem 0.85rem;font-weight:600;font-size:0.9rem;">
|
|
{{ t.settings_push_subscribers }} ({{ push_subscribers.len() }})
|
|
</summary>
|
|
<div style="padding:0 0.85rem 0.85rem;overflow-x:auto;">
|
|
{% if push_subscribers.is_empty() %}
|
|
<p class="help">{{ t.settings_push_no_subscribers }}</p>
|
|
{% else %}
|
|
<table class="table is-fullwidth is-striped is-narrow" style="font-size:0.8rem;background:transparent;">
|
|
<thead><tr>
|
|
<th>{{ t.settings_push_client }}</th>
|
|
<th>{{ t.settings_push_devices }}</th>
|
|
<th>{{ t.settings_push_language }}</th>
|
|
<th>{{ t.settings_push_updated }}</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
{% for subscriber in &push_subscribers %}
|
|
<tr>
|
|
<td>{{ subscriber.client_name }}</td>
|
|
<td>{{ subscriber.device_count }}</td>
|
|
<td>{{ subscriber.languages }}</td>
|
|
<td style="white-space:nowrap;">{{ subscriber.last_updated }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% endif %}
|
|
</div>
|
|
</details>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_telegram_bot_token }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="telegram_bot_token" value="{% for s in &settings %}{% if s.key == "telegram_bot_token" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="subtitle is-6 mb-2 has-text-grey" style="margin-top:1.25rem;">{{ t.settings_section_captcha }}</h3>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_turnstile_site_key }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="turnstile_site_key" value="{% for s in &settings %}{% if s.key == "turnstile_site_key" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_turnstile_secret_key }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="turnstile_secret_key" value="{% for s in &settings %}{% if s.key == "turnstile_secret_key" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="subtitle is-6 mb-2 has-text-grey" style="margin-top:1.25rem;">{{ t.settings_section_oidc }}</h3>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_oidc_issuer_url }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="oidc_issuer_url" placeholder="https://keycloak.example.com/realms/myrealm" value="{% for s in &settings %}{% if s.key == "oidc_issuer_url" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_oidc_client_id }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="oidc_client_id" value="{% for s in &settings %}{% if s.key == "oidc_client_id" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_oidc_client_secret }}</label>
|
|
<div class="control">
|
|
<input class="input" type="password" name="oidc_client_secret" value="{% for s in &settings %}{% if s.key == "oidc_client_secret" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label">{{ t.settings_oidc_allowed_groups }}</label>
|
|
<div class="control">
|
|
<input class="input" type="text" name="oidc_allowed_groups" placeholder="admins, web-petting" value="{% for s in &settings %}{% if s.key == "oidc_allowed_groups" %}{{ s.value }}{% endif %}{% endfor %}">
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="checkbox">
|
|
<input type="checkbox" name="auth_password_enabled" value="true"{% if auth_password_checked %} checked{% endif %}>
|
|
{{ t.settings_auth_password_enabled }}
|
|
</label>
|
|
</div>
|
|
<div class="field">
|
|
<label class="checkbox">
|
|
<input type="checkbox" name="auth_sso_enabled" value="true"{% if auth_sso_checked %} checked{% endif %}>
|
|
{{ t.settings_auth_sso_enabled }}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<button type="submit" class="button is-primary" style="margin-top:1.5rem;">{{ t.settings_save }}</button>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
var COLORS = [
|
|
'rgba(124,108,255,0.18)',
|
|
'rgba(255,82,135,0.15)',
|
|
'rgba(255,140,38,0.18)',
|
|
'rgba(0,180,150,0.15)',
|
|
'rgba(77,166,255,0.18)',
|
|
'rgba(255,179,64,0.18)',
|
|
'rgba(176,108,255,0.16)',
|
|
'rgba(34,180,130,0.16)',
|
|
];
|
|
|
|
var ta = document.getElementById('seoKeywordsInput');
|
|
var preview = document.getElementById('seoPreview');
|
|
|
|
function esc(s) {
|
|
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
}
|
|
|
|
function render() {
|
|
var text = ta.value.trim();
|
|
if (!text) { preview.style.display = 'none'; return; }
|
|
|
|
var parts = text.split(',');
|
|
var html = '';
|
|
parts.forEach(function(part, i) {
|
|
var word = part.trim();
|
|
if (word) {
|
|
var color = COLORS[i % COLORS.length];
|
|
html += '<span style="background:' + color + ';border-radius:4px;padding:2px 6px;margin:2px;">' + esc(word) + '</span>';
|
|
}
|
|
if (i < parts.length - 1) {
|
|
html += '<span style="color:#ccc;font-size:0.8em;margin:0 1px">,</span>';
|
|
}
|
|
});
|
|
|
|
preview.innerHTML = html;
|
|
preview.style.display = 'block';
|
|
}
|
|
|
|
ta.addEventListener('input', render);
|
|
render();
|
|
})();
|
|
</script>
|
|
{% endblock %}
|