Metabolizer Blog Pipeline - integrated CMS for SecuBox: - Gitea: Mirror GitHub repos, store blog content - Streamlit: CMS app with markdown editor and live preview - HexoJS: Static site generator (clean → generate → publish) - Webhooks: Auto-rebuild on git push - Portal: Static blog served at /blog/ Pipeline: Edit in Streamlit CMS → Push to Gitea → Build with Hexo → Publish Packages: - secubox-app-streamlit: Streamlit server with LXC container - luci-app-streamlit: LuCI dashboard for Streamlit apps - secubox-app-metabolizer: CMS pipeline orchestrator CMS Features: - Two-column markdown editor with live preview - YAML front matter editor - Post management (drafts, publish, unpublish) - Media library with image upload - Git sync and Hexo build controls - Cyberpunk theme styling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
240 lines
7.8 KiB
JavaScript
240 lines
7.8 KiB
JavaScript
'use strict';
|
|
'require view';
|
|
'require ui';
|
|
'require dom';
|
|
'require streamlit.api as api';
|
|
|
|
return view.extend({
|
|
configData: null,
|
|
|
|
load: function() {
|
|
return api.getConfig().then(function(config) {
|
|
return config;
|
|
});
|
|
},
|
|
|
|
render: function(configData) {
|
|
var self = this;
|
|
this.configData = configData || {};
|
|
|
|
// Inject CSS
|
|
var cssLink = E('link', {
|
|
'rel': 'stylesheet',
|
|
'type': 'text/css',
|
|
'href': L.resource('streamlit/dashboard.css')
|
|
});
|
|
|
|
var container = E('div', { 'class': 'streamlit-dashboard' }, [
|
|
cssLink,
|
|
this.renderHeader(),
|
|
E('div', { 'class': 'st-main-grid' }, [
|
|
this.renderMainSettings(),
|
|
this.renderServerSettings()
|
|
])
|
|
]);
|
|
|
|
return container;
|
|
},
|
|
|
|
renderHeader: function() {
|
|
var self = this;
|
|
|
|
return E('div', { 'class': 'st-header' }, [
|
|
E('div', { 'class': 'st-header-content' }, [
|
|
E('div', { 'class': 'st-logo' }, '\u2699\uFE0F'),
|
|
E('div', {}, [
|
|
E('h1', { 'class': 'st-title' }, _('SETTINGS')),
|
|
E('p', { 'class': 'st-subtitle' }, _('Configure Streamlit Platform options'))
|
|
]),
|
|
E('div', { 'class': 'st-btn-group' }, [
|
|
E('button', {
|
|
'class': 'st-btn st-btn-success',
|
|
'click': function() { self.saveSettings(); }
|
|
}, [E('span', {}, '\uD83D\uDCBE'), ' ' + _('Save Settings')])
|
|
])
|
|
])
|
|
]);
|
|
},
|
|
|
|
renderMainSettings: function() {
|
|
var config = this.configData.main || {};
|
|
|
|
return E('div', { 'class': 'st-card' }, [
|
|
E('div', { 'class': 'st-card-header' }, [
|
|
E('div', { 'class': 'st-card-title' }, [
|
|
E('span', {}, '\uD83D\uDD27'),
|
|
' ' + _('Main Settings')
|
|
])
|
|
]),
|
|
E('div', { 'class': 'st-card-body' }, [
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Enabled')),
|
|
E('select', {
|
|
'class': 'st-form-select',
|
|
'id': 'cfg-enabled'
|
|
}, [
|
|
E('option', { 'value': '1', 'selected': config.enabled }, _('Enabled')),
|
|
E('option', { 'value': '0', 'selected': !config.enabled }, _('Disabled'))
|
|
])
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('HTTP Port')),
|
|
E('input', {
|
|
'type': 'number',
|
|
'class': 'st-form-input',
|
|
'id': 'cfg-http_port',
|
|
'value': config.http_port || 8501,
|
|
'min': 1,
|
|
'max': 65535
|
|
})
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('HTTP Host')),
|
|
E('input', {
|
|
'type': 'text',
|
|
'class': 'st-form-input',
|
|
'id': 'cfg-http_host',
|
|
'value': config.http_host || '0.0.0.0',
|
|
'placeholder': '0.0.0.0'
|
|
})
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Data Path')),
|
|
E('input', {
|
|
'type': 'text',
|
|
'class': 'st-form-input',
|
|
'id': 'cfg-data_path',
|
|
'value': config.data_path || '/srv/streamlit',
|
|
'placeholder': '/srv/streamlit'
|
|
})
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Memory Limit')),
|
|
E('select', {
|
|
'class': 'st-form-select',
|
|
'id': 'cfg-memory_limit'
|
|
}, [
|
|
E('option', { 'value': '256M', 'selected': config.memory_limit === '256M' }, '256 MB'),
|
|
E('option', { 'value': '512M', 'selected': config.memory_limit === '512M' || !config.memory_limit }, '512 MB'),
|
|
E('option', { 'value': '1G', 'selected': config.memory_limit === '1G' }, '1 GB'),
|
|
E('option', { 'value': '2G', 'selected': config.memory_limit === '2G' }, '2 GB')
|
|
])
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Active App')),
|
|
E('input', {
|
|
'type': 'text',
|
|
'class': 'st-form-input',
|
|
'id': 'cfg-active_app',
|
|
'value': config.active_app || 'hello',
|
|
'placeholder': 'hello'
|
|
})
|
|
])
|
|
])
|
|
]);
|
|
},
|
|
|
|
renderServerSettings: function() {
|
|
var config = this.configData.server || {};
|
|
|
|
return E('div', { 'class': 'st-card' }, [
|
|
E('div', { 'class': 'st-card-header' }, [
|
|
E('div', { 'class': 'st-card-title' }, [
|
|
E('span', {}, '\uD83C\uDFA8'),
|
|
' ' + _('Server & Theme')
|
|
])
|
|
]),
|
|
E('div', { 'class': 'st-card-body' }, [
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Headless Mode')),
|
|
E('select', {
|
|
'class': 'st-form-select',
|
|
'id': 'cfg-headless'
|
|
}, [
|
|
E('option', { 'value': 'true', 'selected': config.headless !== false }, _('Enabled (recommended)')),
|
|
E('option', { 'value': 'false', 'selected': config.headless === false }, _('Disabled'))
|
|
])
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Usage Statistics')),
|
|
E('select', {
|
|
'class': 'st-form-select',
|
|
'id': 'cfg-gather_stats'
|
|
}, [
|
|
E('option', { 'value': 'false', 'selected': !config.browser_gather_usage_stats }, _('Disabled (recommended)')),
|
|
E('option', { 'value': 'true', 'selected': config.browser_gather_usage_stats }, _('Enabled'))
|
|
])
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Theme Base')),
|
|
E('select', {
|
|
'class': 'st-form-select',
|
|
'id': 'cfg-theme_base'
|
|
}, [
|
|
E('option', { 'value': 'dark', 'selected': config.theme_base === 'dark' || !config.theme_base }, _('Dark')),
|
|
E('option', { 'value': 'light', 'selected': config.theme_base === 'light' }, _('Light'))
|
|
])
|
|
]),
|
|
E('div', { 'class': 'st-form-group' }, [
|
|
E('label', { 'class': 'st-form-label' }, _('Primary Color')),
|
|
E('div', { 'style': 'display: flex; gap: 10px; align-items: center;' }, [
|
|
E('input', {
|
|
'type': 'color',
|
|
'id': 'cfg-theme_primary_picker',
|
|
'value': config.theme_primary_color || '#00ffff',
|
|
'style': 'width: 50px; height: 40px; border: none; background: none; cursor: pointer;',
|
|
'change': function() {
|
|
document.getElementById('cfg-theme_primary').value = this.value;
|
|
}
|
|
}),
|
|
E('input', {
|
|
'type': 'text',
|
|
'class': 'st-form-input',
|
|
'id': 'cfg-theme_primary',
|
|
'value': config.theme_primary_color || '#0ff',
|
|
'placeholder': '#0ff',
|
|
'style': 'flex: 1;',
|
|
'change': function() {
|
|
document.getElementById('cfg-theme_primary_picker').value = this.value;
|
|
}
|
|
})
|
|
])
|
|
]),
|
|
E('div', { 'style': 'margin-top: 20px; padding: 16px; background: rgba(0, 255, 255, 0.05); border-radius: 8px; border: 1px solid rgba(0, 255, 255, 0.2);' }, [
|
|
E('p', { 'style': 'color: #0ff; font-size: 13px; margin: 0;' }, [
|
|
E('strong', {}, _('Note: ')),
|
|
_('Changes will take effect after restarting the Streamlit service.')
|
|
])
|
|
])
|
|
])
|
|
]);
|
|
},
|
|
|
|
saveSettings: function() {
|
|
var self = this;
|
|
|
|
var config = {
|
|
enabled: document.getElementById('cfg-enabled').value,
|
|
http_port: parseInt(document.getElementById('cfg-http_port').value, 10),
|
|
http_host: document.getElementById('cfg-http_host').value,
|
|
data_path: document.getElementById('cfg-data_path').value,
|
|
memory_limit: document.getElementById('cfg-memory_limit').value,
|
|
active_app: document.getElementById('cfg-active_app').value,
|
|
headless: document.getElementById('cfg-headless').value,
|
|
browser_gather_usage_stats: document.getElementById('cfg-gather_stats').value,
|
|
theme_base: document.getElementById('cfg-theme_base').value,
|
|
theme_primary_color: document.getElementById('cfg-theme_primary').value
|
|
};
|
|
|
|
api.saveConfig(config).then(function(result) {
|
|
if (result && result.success) {
|
|
ui.addNotification(null, E('p', {}, _('Settings saved successfully')), 'success');
|
|
} else {
|
|
ui.addNotification(null, E('p', {}, result.message || _('Failed to save settings')), 'error');
|
|
}
|
|
}).catch(function(err) {
|
|
ui.addNotification(null, E('p', {}, _('Failed to save: ') + err.message), 'error');
|
|
});
|
|
}
|
|
});
|