{% extends "base.twig" %}
{% block title %}{{ t('ui.admin.mrc_settings.page_title') }}{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">{{ t('ui.admin.mrc_settings.heading') }}</h1>
<div></div>
</div>
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> <strong>{{ t('ui.admin.mrc_settings.note_label') }}:</strong> {{ t('ui.admin.mrc_settings.note_text') }}
{{ t('ui.admin.mrc_settings.note_enable_text') }} <a href="/admin/webdoors" class="alert-link">{{ t('ui.admin.mrc_settings.webdoors_admin') }}</a>.
</div>
<div class="row g-4" id="mrcSettingsCards">
<!-- General Settings -->
<div class="col-lg-6">
<div class="card shadow h-100">
<div class="card-header">
<h6 class="mb-0">{{ t('ui.admin.mrc_settings.general_settings') }}</h6>
</div>
<div class="card-body">
<div id="mrcGeneralAlert"></div>
<form id="mrcGeneralForm">
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="mrcEnabled">
<label class="form-check-label" for="mrcEnabled">{{ t('ui.admin.mrc_settings.enable_daemon') }}</label>
<small class="form-text d-block text-muted">
</small>
</div>
<hr>
<h6 class="mb-3">{{ t('ui.admin.mrc_settings.server_connection') }}</h6>
<div class="mb-3">
<label class="form-label" for="mrcServerHost">{{ t('ui.admin.mrc_settings.server_host') }}</label>
<input type="text" class="form-control" id="mrcServerHost" placeholder="{{ t('ui.admin.mrc_settings.placeholder.server_host', {}, locale, ['common']) }}">
</div>
<div class="mb-3">
<label class="form-label" for="mrcServerPort">{{ t('ui.admin.mrc_settings.server_port_non_ssl') }}</label>
<input type="number" class="form-control" id="mrcServerPort" min="1" max="65535" value="50000">
</div>
<div class="mb-3">
<label class="form-label" for="mrcServerSslPort">{{ t('ui.admin.mrc_settings.server_port_ssl') }}</label>
<input type="number" class="form-control" id="mrcServerSslPort" min="1" max="65535" value="50001">
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="mrcUseSsl" checked>
<label class="form-check-label" for="mrcUseSsl">{{ t('ui.admin.mrc_settings.use_ssl_connection') }}</label>
<small class="form-text d-block text-muted">
{{ t('ui.admin.mrc_settings.use_ssl_help') }}
</small>
</div>
<button type="button" class="btn btn-primary" onclick="saveMrcSettings()">
{{ t('ui.admin.mrc_settings.save_general_settings') }}
</button>
</form>
</div>
</div>
</div>
<!-- BBS Identity -->
<div class="col-lg-6">
<div class="card shadow h-100">
<div class="card-header">
<h6 class="mb-0">{{ t('ui.admin.mrc_settings.bbs_identity') }}</h6>
</div>
<div class="card-body">
<div id="mrcIdentityAlert"></div>
<form id="mrcIdentityForm">
<div class="mb-3">
<label class="form-label" for="mrcBbsName">{{ t('ui.admin.mrc_settings.bbs_name') }}</label>
<input type="text" class="form-control" id="mrcBbsName" maxlength="64" placeholder="{{ t('ui.admin.mrc_settings.placeholder.bbs_name', {}, locale, ['common']) }}">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.bbs_name_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcPlatform">{{ t('ui.admin.mrc_settings.platform_info') }}</label>
<input type="text" class="form-control" id="mrcPlatform" placeholder="{{ t('ui.admin.mrc_settings.placeholder.platform', {}, locale, ['common']) }}">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.platform_info_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcSysop">{{ t('ui.admin.mrc_settings.sysop_name') }}</label>
<input type="text" class="form-control" id="mrcSysop" placeholder="{{ t('ui.admin.mrc_settings.placeholder.sysop', {}, locale, ['common']) }}">
</div>
<hr>
<h6 class="mb-3">{{ t('ui.admin.mrc_settings.bbs_information') }}</h6>
<div class="mb-3">
<label class="form-label" for="mrcWebsite">{{ t('ui.admin.mrc_settings.website_url') }}</label>
<input type="url" class="form-control" id="mrcWebsite" placeholder="{{ t('ui.admin.mrc_settings.placeholder.website', {}, locale, ['common']) }}">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.info_optional_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcTelnet">{{ t('ui.admin.mrc_settings.telnet_address') }}</label>
<input type="text" class="form-control" id="mrcTelnet" placeholder="{{ t('ui.admin.mrc_settings.placeholder.telnet', {}, locale, ['common']) }}">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.info_optional_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcDescription">{{ t('ui.admin.mrc_settings.bbs_description') }}</label>
<textarea class="form-control" id="mrcDescription" rows="3" placeholder="{{ t('ui.admin.mrc_settings.placeholder.description', {}, locale, ['common']) }}"></textarea>
</div>
<button type="button" class="btn btn-primary" onclick="saveMrcSettings()">
{{ t('ui.admin.mrc_settings.save_identity_settings') }}
</button>
</form>
</div>
</div>
</div>
<!-- Connection Settings -->
<div class="col-lg-6">
<div class="card shadow h-100">
<div class="card-header">
<h6 class="mb-0">{{ t('ui.admin.mrc_settings.connection_settings') }}</h6>
</div>
<div class="card-body">
<div id="mrcConnectionAlert"></div>
<form id="mrcConnectionForm">
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="mrcAutoReconnect" checked>
<label class="form-check-label" for="mrcAutoReconnect">{{ t('ui.admin.mrc_settings.auto_reconnect') }}</label>
<small class="form-text d-block text-muted">
{{ t('ui.admin.mrc_settings.auto_reconnect_help') }}
</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcReconnectDelay">{{ t('ui.admin.mrc_settings.reconnect_delay') }}</label>
<input type="number" class="form-control" id="mrcReconnectDelay" min="5" max="300" value="30">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.reconnect_delay_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcPingInterval">{{ t('ui.admin.mrc_settings.ping_interval') }}</label>
<input type="number" class="form-control" id="mrcPingInterval" min="30" max="300" value="60">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.ping_interval_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcKeepaliveTimeout">{{ t('ui.admin.mrc_settings.keepalive_timeout') }}</label>
<input type="number" class="form-control" id="mrcKeepaliveTimeout" min="60" max="600" value="125">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.keepalive_timeout_help') }}</small>
</div>
<button type="button" class="btn btn-primary" onclick="saveMrcSettings()">
{{ t('ui.admin.mrc_settings.save_connection_settings') }}
</button>
</form>
</div>
</div>
</div>
<!-- Room Settings -->
<div class="col-lg-6">
<div class="card shadow h-100">
<div class="card-header">
<h6 class="mb-0">{{ t('ui.admin.mrc_settings.room_settings') }}</h6>
</div>
<div class="card-body">
<div id="mrcRoomAlert"></div>
<form id="mrcRoomForm">
<div class="mb-3">
<label class="form-label" for="mrcDefaultRoom">{{ t('ui.admin.mrc_settings.default_room') }}</label>
<input type="text" class="form-control" id="mrcDefaultRoom" value="lobby">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.default_room_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcAutoJoinRooms">{{ t('ui.admin.mrc_settings.auto_join_rooms') }}</label>
<input type="text" class="form-control" id="mrcAutoJoinRooms" value="lobby">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.auto_join_rooms_help') }}</small>
</div>
<hr>
<h6 class="mb-3">{{ t('ui.admin.mrc_settings.message_settings') }}</h6>
<div class="mb-3">
<label class="form-label" for="mrcMaxMessageLength">{{ t('ui.admin.mrc_settings.max_message_length') }}</label>
<input type="number" class="form-control" id="mrcMaxMessageLength" min="80" max="255" value="140">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.max_message_length_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcHistoryLimit">{{ t('ui.admin.mrc_settings.history_limit') }}</label>
<input type="number" class="form-control" id="mrcHistoryLimit" min="100" max="10000" value="1000">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.history_limit_help') }}</small>
</div>
<div class="mb-3">
<label class="form-label" for="mrcPruneAfterDays">{{ t('ui.admin.mrc_settings.prune_after_days') }}</label>
<input type="number" class="form-control" id="mrcPruneAfterDays" min="1" max="365" value="30">
<small class="form-text text-muted">{{ t('ui.admin.mrc_settings.prune_after_days_help') }}</small>
</div>
<button type="button" class="btn btn-primary" onclick="saveMrcSettings()">
{{ t('ui.admin.mrc_settings.save_room_settings') }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
// Global config variable
let mrcConfig = {};
function uiT(key, fallback, params = {}) {
if (window.t) {
return window.t(key, params, fallback);
}
return fallback;
}
// Load MRC configuration on page load
$(document).ready(function() {
loadMrcSettings();
});
/**
* Load MRC settings from admin daemon
*/
async function loadMrcSettings() {
setSettingsLoading('#mrcSettingsCards', true);
try {
const response = await $.ajax({
url: '/admin/api/mrc-settings',
method: 'GET',
dataType: 'json'
});
if (response.success) {
mrcConfig = response.config;
populateMrcForm();
setSettingsLoading('#mrcSettingsCards', false);
} else {
showAlert('mrcGeneralAlert', 'danger', uiT('ui.admin.mrc_settings.load_failed', 'Failed to load MRC settings'));
}
} catch (error) {
console.error('Failed to load MRC settings:', error);
showAlert('mrcGeneralAlert', 'danger', uiT('ui.admin.mrc_settings.load_error', 'Error loading MRC settings'));
}
}
/**
* Populate form fields with loaded config
*/
function populateMrcForm() {
// General settings
$('#mrcEnabled').prop('checked', mrcConfig.enabled || false);
$('#mrcServerHost').val(mrcConfig.server?.host || 'mrc.bottomlessabyss.net');
$('#mrcServerPort').val(mrcConfig.server?.port || 50000);
$('#mrcServerSslPort').val(mrcConfig.server?.ssl_port || 50001);
$('#mrcUseSsl').prop('checked', mrcConfig.server?.use_ssl !== false);
// BBS identity
$('#mrcBbsName').val(mrcConfig.bbs?.name || 'BinktermPHP BBS');
$('#mrcPlatform').val(mrcConfig.bbs?.platform || 'BINKTERMPHP/Linux64/1.3.0');
$('#mrcSysop').val(mrcConfig.bbs?.sysop || 'Sysop');
// BBS info
$('#mrcWebsite').val(mrcConfig.info?.website || '');
$('#mrcTelnet').val(mrcConfig.info?.telnet || '');
$('#mrcDescription').val(mrcConfig.info?.description || '');
// Connection settings
$('#mrcAutoReconnect').prop('checked', mrcConfig.connection?.auto_reconnect !== false);
$('#mrcReconnectDelay').val(mrcConfig.connection?.reconnect_delay || 30);
$('#mrcPingInterval').val(mrcConfig.connection?.ping_interval || 60);
$('#mrcKeepaliveTimeout').val(mrcConfig.connection?.keepalive_timeout || 125);
// Room settings
$('#mrcDefaultRoom').val(mrcConfig.rooms?.default || 'lobby');
$('#mrcAutoJoinRooms').val((mrcConfig.rooms?.auto_join || ['lobby']).join(', '));
// Message settings
$('#mrcMaxMessageLength').val(mrcConfig.messages?.max_length || 140);
$('#mrcHistoryLimit').val(mrcConfig.messages?.history_limit || 1000);
$('#mrcPruneAfterDays').val(mrcConfig.messages?.prune_after_days || 30);
}
/**
* Save MRC settings to admin daemon
*/
async function saveMrcSettings() {
// Collect all form values
const config = {
enabled: $('#mrcEnabled').is(':checked'),
server: {
host: $('#mrcServerHost').val(),
port: parseInt($('#mrcServerPort').val()),
use_ssl: $('#mrcUseSsl').is(':checked'),
ssl_port: parseInt($('#mrcServerSslPort').val())
},
bbs: {
name: $('#mrcBbsName').val(),
platform: $('#mrcPlatform').val(),
sysop: $('#mrcSysop').val()
},
connection: {
auto_reconnect: $('#mrcAutoReconnect').is(':checked'),
reconnect_delay: parseInt($('#mrcReconnectDelay').val()),
ping_interval: parseInt($('#mrcPingInterval').val()),
handshake_timeout: mrcConfig.connection?.handshake_timeout || 1,
keepalive_timeout: parseInt($('#mrcKeepaliveTimeout').val())
},
rooms: {
default: $('#mrcDefaultRoom').val(),
auto_join: $('#mrcAutoJoinRooms').val().split(',').map(r => r.trim()).filter(r => r)
},
messages: {
max_length: parseInt($('#mrcMaxMessageLength').val()),
history_limit: parseInt($('#mrcHistoryLimit').val()),
prune_after_days: parseInt($('#mrcPruneAfterDays').val())
},
info: {
website: $('#mrcWebsite').val(),
telnet: $('#mrcTelnet').val(),
ssh: mrcConfig.info?.ssh || '',
description: $('#mrcDescription').val()
}
};
try {
const response = await $.ajax({
url: '/admin/api/mrc-settings',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ config }),
dataType: 'json'
});
if (response.success) {
const successMessage = window.getApiMessage
? window.getApiMessage(response, uiT('ui.admin.mrc_settings.saved_success', 'MRC settings saved successfully'))
: uiT('ui.admin.mrc_settings.saved_success', 'MRC settings saved successfully');
showAlert('mrcGeneralAlert', 'success', successMessage);
mrcConfig = response.config;
// If enabled status changed, suggest daemon restart
if (config.enabled !== mrcConfig.enabled) {
showAlert('mrcGeneralAlert', 'warning', uiT('ui.admin.mrc_settings.restart_required', 'Settings saved. Restart the daemon to apply changes.'));
}
} else {
showAlert('mrcGeneralAlert', 'danger', uiT('ui.admin.mrc_settings.save_failed', 'Failed to save MRC settings'));
}
} catch (error) {
console.error('Failed to save MRC settings:', error);
showAlert('mrcGeneralAlert', 'danger', uiT('ui.admin.mrc_settings.save_error', 'Error saving MRC settings'));
}
}
/**
* Show alert message
*/
function showAlert(containerId, type, message) {
const alertHtml = `
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
`;
$(`#${containerId}`).html(alertHtml);
// Auto-dismiss after 5 seconds
setTimeout(() => {
const alertEl = document.querySelector(`#${containerId} .alert`);
if (!alertEl) {
return;
}
if (window.bootstrap && window.bootstrap.Alert) {
window.bootstrap.Alert.getOrCreateInstance(alertEl).close();
return;
}
alertEl.remove();
}, 5000);
}
</script>
{% endblock %}
|