Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
save_user_settings,
)
from .app_settings import (is_registration_enabled, set_registration_enabled,
is_ssh_gateway_enabled, set_ssh_gateway_enabled)
is_ssh_gateway_enabled, set_ssh_gateway_enabled,
is_support_links_enabled, set_support_links_enabled)
from .storage_errors import StorageCorruptionError
from .tailscale_ssh import user_can_use_tailscale_ssh
from .version import display_version
Expand Down Expand Up @@ -641,6 +642,9 @@ def inject_url_prefix():
'webssh_version': (
display_version() if current_user.is_authenticated else ''
),
'support_links_enabled': (
current_user.is_authenticated and is_support_links_enabled()
),
}

@app.before_request
Expand Down Expand Up @@ -1735,6 +1739,7 @@ def admin_get_settings():
policies = {name: runtime_feature_status(name) for name in RUNTIME_FEATURES}
return jsonify({'setting_sources': native_setting_sources(), 'runtime_features': policies, **{name: status['enabled'] for name, status in policies.items()}, 'registration_enabled': is_registration_enabled(),
'ssh_gateway_enabled': is_ssh_gateway_enabled(),
'support_links_enabled': is_support_links_enabled(),
'credentials_enabled': is_credentials_enabled(),
'automation_enabled': is_automation_enabled()})

Expand Down Expand Up @@ -1764,6 +1769,9 @@ def admin_set_settings():
if ('ssh_gateway_enabled' in data
and type(data['ssh_gateway_enabled']) is not bool):
return jsonify({'error': 'ssh_gateway_enabled must be a boolean'}), 400
if ('support_links_enabled' in data
and type(data['support_links_enabled']) is not bool):
return jsonify({'error': 'support_links_enabled must be a boolean'}), 400
if 'registration_enabled' in data:
if type(data['registration_enabled']) is not bool:
return jsonify({
Expand All @@ -1786,6 +1794,10 @@ def admin_set_settings():
val = set_ssh_gateway_enabled(data['ssh_gateway_enabled'])
log_info("Admin changed SSH gateway setting",
admin=current_user.username, ssh_gateway_enabled=val)
if 'support_links_enabled' in data:
val = set_support_links_enabled(data['support_links_enabled'])
log_info("Admin changed support links setting",
admin=current_user.username, support_links_enabled=val)
from .app_settings import set_credentials_enabled, set_automation_enabled
for feature, setter in (('credentials_enabled', set_credentials_enabled),
('automation_enabled', set_automation_enabled)):
Expand Down
18 changes: 18 additions & 0 deletions app/app_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'server_operations_enabled',
'tmux_enabled',
'tmux_default',
'support_links_enabled',
)


Expand Down Expand Up @@ -124,6 +125,23 @@ def set_ssh_gateway_enabled(value):
return value


def is_support_links_enabled():
"""Project support links are shown unless an administrator hid them."""
return _load().get('support_links_enabled') is not False


def set_support_links_enabled(value):
if type(value) is not bool:
return False
with storage_lock(f'app-settings:{_SETTINGS_FILE}'):
data = _load_with_lock_held()
data['support_links_enabled'] = value
if not _valid_settings(data):
return False
_save(data)
return value


def set_audit_backup_count(value):
if type(value) is not int or not 1 <= value <= 90:
return False
Expand Down
5 changes: 5 additions & 0 deletions docs/wiki/Configuration-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ or bypass provider, HTTPS, origin, RP-ID or recovery checks. Networking, identit
provider trust, secrets, resource ceilings, mounts and backup recovery paths remain
deployment configuration. Audit retention is already managed in Administration.

Signed-in users see links to support WebSSH development in the account menu
and the Settings side panel. **Project support links** in Administration →
Integrations hides them for all users; the links load nothing from external
sites until a user follows one.

TOTP capability is available by default in 3.0; Admin activation remains off
until explicitly enabled. `TOTP_ENABLED=false` is the optional operator kill
switch. Existing enrolled users and saved activation choices are retained.
Expand Down
25 changes: 25 additions & 0 deletions static/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,31 @@
font-size: 17px;
}

.settings-support-hint {
margin: 0 0 8px;
color: var(--text-muted);
font-size: 12px;
line-height: 1.5;
}

.settings-support-links {
display: grid;
gap: 2px;
margin: 0;
padding: 0;
list-style: none;
}

.settings-support-links .settings-quick-link {
font-size: 13px;
text-decoration: none;
}

.settings-support-links .settings-quick-link:focus-visible {
outline: 2px solid var(--accent-primary);
outline-offset: 1px;
}

.settings-context-note {
display: flex;
align-items: flex-start;
Expand Down
45 changes: 2 additions & 43 deletions static/css/session-workspace.css
Original file line number Diff line number Diff line change
Expand Up @@ -288,45 +288,6 @@
margin: 13px 0;
}

.session-cpu-gauge {
display: grid;
width: 82px;
height: 82px;
padding: 7px;
border-radius: 50%;
background: conic-gradient(var(--gauge-color, var(--accent-primary)) calc(var(--value) * 1%), var(--bg-hover) 0);
place-items: center;
}

.session-cpu-gauge::before {
content: '';
grid-area: 1 / 1;
width: 100%;
height: 100%;
border-radius: 50%;
background: var(--bg-secondary);
}

.session-cpu-gauge > div {
z-index: 1;
grid-area: 1 / 1;
text-align: center;
}

.session-cpu-gauge strong {
display: block;
color: var(--text-primary);
font-family: var(--font-mono);
font-size: 17px;
}

.session-cpu-gauge span {
color: var(--text-muted);
font-size: 9px;
font-weight: 700;
letter-spacing: 0.07em;
}

.session-cpu-chart {
width: 100%;
height: 72px;
Expand Down Expand Up @@ -370,17 +331,15 @@
transition: width 0.35s ease;
}

.session-resource.warning .session-resource-track span,
.session-cpu-gauge.warning {
.session-resource.warning .session-resource-track span {
--gauge-color: var(--warning-color, #d29922);
}

.session-resource.warning .session-resource-track span {
background: var(--warning-color, #d29922);
}

.session-resource.critical .session-resource-track span,
.session-cpu-gauge.critical {
.session-resource.critical .session-resource-track span {
--gauge-color: var(--danger-color, #f85149);
}

Expand Down
6 changes: 3 additions & 3 deletions static/css/sftp-file-manager.css
Original file line number Diff line number Diff line change
Expand Up @@ -3163,7 +3163,7 @@
}

.smb-source-security-note .material-icons {
color: var(--primary-color);
color: var(--accent-primary);
font-size: 18px;
}

Expand All @@ -3175,11 +3175,11 @@
}

.smb-source-status[data-state="error"] {
color: var(--danger-color);
color: var(--error-color);
}

.smb-source-status[data-state="pending"] {
color: var(--primary-color);
color: var(--accent-primary);
}

.smb-source-status[data-state="success"] {
Expand Down
6 changes: 6 additions & 0 deletions static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4157,6 +4157,12 @@ body.keyboard-open.notepad-focused .notepad-panel {
display: none;
}

.account-item .account-item-external {
margin-left: auto;
color: var(--text-secondary);
font-size: 15px;
}

.account-version {
margin: 6px 4px 0;
padding: 8px 10px 4px;
Expand Down
4 changes: 2 additions & 2 deletions static/js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,7 @@
gateway.checked = data.ssh_gateway_enabled === true;
gateway.disabled = false;
}
for (const [id, key] of [['settingCredentials', 'credentials_enabled'], ['settingAutomation', 'automation_enabled']]) {
for (const [id, key] of [['settingCredentials', 'credentials_enabled'], ['settingAutomation', 'automation_enabled'], ['settingSupportLinks', 'support_links_enabled']]) {
const input = document.getElementById(id);
if (input && !settingsWrites.has(key)) {
input.checked = data[key] === true;
Expand Down Expand Up @@ -1551,7 +1551,7 @@
target.disabled = false;
}
});
for (const [id, key] of [['settingCredentials', 'credentials_enabled'], ['settingAutomation', 'automation_enabled']]) {
for (const [id, key] of [['settingCredentials', 'credentials_enabled'], ['settingAutomation', 'automation_enabled'], ['settingSupportLinks', 'support_links_enabled']]) {
document.getElementById(id)?.addEventListener('change', async (event) => {
const input = event.target;
const enabled = input.checked;
Expand Down
2 changes: 1 addition & 1 deletion static/js/drag-drop-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ class DragDropManager {
if (session) {
hint.textContent = `${session.username}@${session.host} — ${this.t('dropUpload.chooseFolder', 'Choose a destination folder after dropping.')}`;
} else {
hint.textContent = 'Connect to a server first';
hint.textContent = this.t('dropUpload.noSession', 'Connect to a server first');
}
}
}
Expand Down
Loading
Loading