profiles: add presets and wizard integration
This commit is contained in:
parent
eab24f9609
commit
3c0003614d
@ -22,6 +22,8 @@ These notes summarize the repository structure, conventions, and supporting tool
|
||||
- `DOCS/` + `docs/`: mirrored, versioned documentation tree (design system, prompts, module templates, validation, permissions, etc.).
|
||||
- `EXAMPLES/` and `templates/`: snippets and scaffolding.
|
||||
- CI workflows live in `.github/workflows/` (referenced from README badges).
|
||||
- **Profiles & App Manifests**
|
||||
- App manifests (`/usr/share/secubox/plugins/`) and profile presets (`/usr/share/secubox/profiles/`) feed both the SecuBox wizard UI and the `secubox-app` CLI for automated installs.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -223,6 +223,11 @@ Pointer: see `docs/embedded/vhost-manager.md` for the canonical version.
|
||||
|
||||
Pointer: see `docs/embedded/app-store.md` for the canonical version.
|
||||
|
||||
#### **embedded/wizard-profiles.md** 🧭
|
||||
*First-run wizard and OS-like profiles.*
|
||||
|
||||
Pointer: see `docs/embedded/wizard-profiles.md` for the canonical version.
|
||||
|
||||
---
|
||||
|
||||
### 5. Tools & Scripts Documentation
|
||||
|
||||
74
DOCS/embedded/wizard-profiles.md
Normal file
74
DOCS/embedded/wizard-profiles.md
Normal file
@ -0,0 +1,74 @@
|
||||
# SecuBox Wizard & Profiles
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Last Updated:** 2025-12-28
|
||||
**Status:** Active
|
||||
|
||||
The SecuBox hub now includes a guided setup wizard (LuCI → SecuBox → Wizard) and profile system. Use them to finish the first-run checklist, review app manifests, and apply predefined OS-like configurations (Home, Lab, Hardened, Gateway + DMZ).
|
||||
|
||||
---
|
||||
|
||||
## First-Run Checklist
|
||||
|
||||
The wizard queries `luci.secubox`’s `first_run_status` ubus method to determine whether critical items are configured:
|
||||
|
||||
1. **Administrator password** – links to the LuCI password page.
|
||||
2. **Timezone** – dropdown populated with common timezones; applying calls `apply_first_run` with `{ timezone: "Europe/Paris" }`.
|
||||
3. **Storage path** – defaults to `/srv/secubox`; prepares the directory and stores it in `uci set secubox.main.storage_path`.
|
||||
4. **Network mode** – uses the existing Network Modes RPC to switch between `router` and `dmz` presets.
|
||||
|
||||
Each action can be run independently and is idempotent.
|
||||
|
||||
---
|
||||
|
||||
## App Wizards (Manifests)
|
||||
|
||||
Apps ship manifests under `/usr/share/secubox/plugins/<id>/manifest.json`. `secubox-app` (installed at `/usr/sbin/secubox-app`) uses the same manifests for CLI installs, and the wizard consumes the `wizard.fields` section to build forms. Example snippet:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "zigbee2mqtt",
|
||||
"wizard": {
|
||||
"uci": { "config": "zigbee2mqtt", "section": "main" },
|
||||
"fields": [
|
||||
{ "id": "serial_port", "label": "Serial Port", "uci_option": "serial_port" },
|
||||
{ "id": "mqtt_host", "label": "MQTT Host", "uci_option": "mqtt_host" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Clicking “Configure” opens a modal that writes the provided values into the specified UCI section.
|
||||
|
||||
---
|
||||
|
||||
## Profiles
|
||||
|
||||
Profiles are stored as JSON in `/usr/share/secubox/profiles/` and can bundle:
|
||||
|
||||
- `network_mode`: target SecuBox network mode (`router`, `dmz`, …)
|
||||
- `apps`: manifest IDs to install via `secubox-app install <id>`
|
||||
- `packages`: additional packages to ensure via opkg/apk
|
||||
- `uci`: array of `{config, section, option, value}` entries applied via UCI
|
||||
|
||||
Baseline profiles:
|
||||
|
||||
| ID | Description | Highlights |
|
||||
|----|-------------|------------|
|
||||
| `home` | Home router + Zigbee2MQTT | Router mode, installs Zigbee2MQTT + Netdata |
|
||||
| `lab` | Monitoring lab | Router mode, ensures Netifyd & Bandwidth Manager |
|
||||
| `hardened` | Security-focused | Enables CrowdSec + Client Guardian |
|
||||
| `gateway_dmz` | Router + DMZ segment | Switches to DMZ mode and enables VHost manager |
|
||||
|
||||
`apply_profile` automatically tars `/etc/config` to `/etc/secubox-profiles/backups/` before modifying settings, so the **Rollback last profile** button (or `rollback_profile` RPC) instantly restores prior UCI files.
|
||||
|
||||
---
|
||||
|
||||
## CLI References
|
||||
|
||||
- `secubox-app list|install|status` – manage app manifests (installed by `luci-app-secubox`).
|
||||
- `ubus call luci.secubox list_profiles` – enumerate available profile manifests.
|
||||
- `ubus call luci.secubox apply_profile '{"profile_id":"home"}'` – apply a preset programmatically.
|
||||
- `secubox-app` respects `SECUBOX_PLUGINS_DIR` if you need to point to custom manifest trees.
|
||||
|
||||
Combine the wizard UI with these commands to automate deployments or build higher-level orchestration (e.g., App Store pages, onboarding scripts).
|
||||
@ -28,6 +28,9 @@ These notes capture the current repository structure, conventions, and supportin
|
||||
- **Docs**
|
||||
Two mirrored trees (`docs/` and uppercase `DOCS/`) feed MkDocs and the GitHub wiki. All Markdown follows the metadata template defined in `docs/documentation-index.md`.
|
||||
|
||||
- **Profiles & App Manifests**
|
||||
App manifests now live under `/usr/share/secubox/plugins/`, profiles under `/usr/share/secubox/profiles/`. `luci-app-secubox` exposes both through the wizard (UI) and `secubox-app` CLI (automation).
|
||||
|
||||
---
|
||||
|
||||
## 2. Target Platforms & Build System
|
||||
|
||||
@ -247,6 +247,18 @@ Follow this template when creating or revising documentation:
|
||||
|
||||
**Size:** Short (~120 lines)
|
||||
|
||||
#### **embedded/wizard-profiles.md** 🧭
|
||||
*First-run wizard + OS-like profiles.*
|
||||
|
||||
**Contents:**
|
||||
- First-run checklist (password, timezone, storage, network mode)
|
||||
- Using manifest-driven app wizards
|
||||
- Baseline profiles (Home, Lab, Hardened, Gateway+DMZ) and rollback mechanics
|
||||
|
||||
**When to use:** Guiding new deployments or applying pre-made bundles.
|
||||
|
||||
**Size:** Short (~130 lines)
|
||||
|
||||
---
|
||||
|
||||
### 5. Tools & Scripts Documentation
|
||||
|
||||
74
docs/embedded/wizard-profiles.md
Normal file
74
docs/embedded/wizard-profiles.md
Normal file
@ -0,0 +1,74 @@
|
||||
# SecuBox Wizard & Profiles
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Last Updated:** 2025-12-28
|
||||
**Status:** Active
|
||||
|
||||
The SecuBox hub now includes a guided setup wizard (LuCI → SecuBox → Wizard) and profile system. Use them to finish the first-run checklist, review app manifests, and apply predefined OS-like configurations (Home, Lab, Hardened, Gateway + DMZ).
|
||||
|
||||
---
|
||||
|
||||
## First-Run Checklist
|
||||
|
||||
The wizard queries `luci.secubox`’s `first_run_status` ubus method to determine whether critical items are configured:
|
||||
|
||||
1. **Administrator password** – links to the LuCI password page.
|
||||
2. **Timezone** – dropdown populated with common timezones; applying calls `apply_first_run` with `{ timezone: "Europe/Paris" }`.
|
||||
3. **Storage path** – defaults to `/srv/secubox`; prepares the directory and stores it in `uci set secubox.main.storage_path`.
|
||||
4. **Network mode** – uses the existing Network Modes RPC to switch between `router` and `dmz` presets.
|
||||
|
||||
Each action can be run independently and is idempotent.
|
||||
|
||||
---
|
||||
|
||||
## App Wizards (Manifests)
|
||||
|
||||
Apps ship manifests under `/usr/share/secubox/plugins/<id>/manifest.json`. `secubox-app` (installed at `/usr/sbin/secubox-app`) uses the same manifests for CLI installs, and the wizard consumes the `wizard.fields` section to build forms. Example snippet:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "zigbee2mqtt",
|
||||
"wizard": {
|
||||
"uci": { "config": "zigbee2mqtt", "section": "main" },
|
||||
"fields": [
|
||||
{ "id": "serial_port", "label": "Serial Port", "uci_option": "serial_port" },
|
||||
{ "id": "mqtt_host", "label": "MQTT Host", "uci_option": "mqtt_host" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Clicking “Configure” opens a modal that writes the provided values into the specified UCI section.
|
||||
|
||||
---
|
||||
|
||||
## Profiles
|
||||
|
||||
Profiles are stored as JSON in `/usr/share/secubox/profiles/` and can bundle:
|
||||
|
||||
- `network_mode`: target SecuBox network mode (`router`, `dmz`, …)
|
||||
- `apps`: manifest IDs to install via `secubox-app install <id>`
|
||||
- `packages`: additional packages to ensure via opkg/apk
|
||||
- `uci`: array of `{config, section, option, value}` entries applied via UCI
|
||||
|
||||
Baseline profiles:
|
||||
|
||||
| ID | Description | Highlights |
|
||||
|----|-------------|------------|
|
||||
| `home` | Home router + Zigbee2MQTT | Router mode, installs Zigbee2MQTT + Netdata |
|
||||
| `lab` | Monitoring lab | Router mode, ensures Netifyd & Bandwidth Manager |
|
||||
| `hardened` | Security-focused | Enables CrowdSec + Client Guardian |
|
||||
| `gateway_dmz` | Router + DMZ segment | Switches to DMZ mode and enables VHost manager |
|
||||
|
||||
`apply_profile` automatically tars `/etc/config` to `/etc/secubox-profiles/backups/` before modifying settings, so the **Rollback last profile** button (or `rollback_profile` RPC) instantly restores prior UCI files.
|
||||
|
||||
---
|
||||
|
||||
## CLI References
|
||||
|
||||
- `secubox-app list|install|status` – manage app manifests (installed by `luci-app-secubox`).
|
||||
- `ubus call luci.secubox list_profiles` – enumerate available profile manifests.
|
||||
- `ubus call luci.secubox apply_profile '{"profile_id":"home"}'` – apply a preset programmatically.
|
||||
- `secubox-app` respects `SECUBOX_PLUGINS_DIR` if you need to point to custom manifest trees.
|
||||
|
||||
Combine the wizard UI with these commands to automate deployments or build higher-level orchestration (e.g., App Store pages, onboarding scripts).
|
||||
@ -26,6 +26,10 @@ define Package/$(PKG_NAME)/install
|
||||
$(call Package/luci/install,$(1))
|
||||
$(INSTALL_DIR) $(1)/usr/share/secubox/plugins/zigbee2mqtt
|
||||
$(INSTALL_DATA) $(CURDIR)/../plugins/zigbee2mqtt/manifest.json $(1)/usr/share/secubox/plugins/zigbee2mqtt/manifest.json
|
||||
$(INSTALL_DIR) $(1)/usr/share/secubox/profiles
|
||||
for file in $(CURDIR)/../profiles/*.json; do \
|
||||
$(INSTALL_DATA) $$file $(1)/usr/share/secubox/profiles/$$(basename $$file); \
|
||||
done
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(CURDIR)/../secubox-tools/secubox-app $(1)/usr/sbin/secubox-app
|
||||
endef
|
||||
|
||||
@ -174,6 +174,23 @@ var callApplyAppWizard = rpc.declare({
|
||||
params: ['app_id', 'values']
|
||||
});
|
||||
|
||||
var callListProfiles = rpc.declare({
|
||||
object: 'luci.secubox',
|
||||
method: 'list_profiles',
|
||||
expect: { profiles: [] }
|
||||
});
|
||||
|
||||
var callApplyProfile = rpc.declare({
|
||||
object: 'luci.secubox',
|
||||
method: 'apply_profile',
|
||||
params: ['profile_id']
|
||||
});
|
||||
|
||||
var callRollbackProfile = rpc.declare({
|
||||
object: 'luci.secubox',
|
||||
method: 'rollback_profile'
|
||||
});
|
||||
|
||||
function formatUptime(seconds) {
|
||||
if (!seconds) return '0s';
|
||||
var d = Math.floor(seconds / 86400);
|
||||
@ -222,6 +239,9 @@ return baseclass.extend({
|
||||
listApps: callListApps,
|
||||
getAppManifest: callGetAppManifest,
|
||||
applyAppWizard: callApplyAppWizard,
|
||||
listProfiles: callListProfiles,
|
||||
applyProfile: callApplyProfile,
|
||||
rollbackProfile: callRollbackProfile,
|
||||
// Utilities
|
||||
formatUptime: formatUptime,
|
||||
formatBytes: formatBytes
|
||||
|
||||
@ -22,18 +22,21 @@ return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
API.getFirstRunStatus(),
|
||||
API.listApps()
|
||||
API.listApps(),
|
||||
API.listProfiles()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(payload) {
|
||||
this.firstRun = payload[0] || {};
|
||||
this.appList = (payload[1] && payload[1].apps) || [];
|
||||
this.profileList = (payload[2] && payload[2].profiles) || [];
|
||||
var container = E('div', { 'class': 'secubox-wizard-page' }, [
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
|
||||
SecuNav.renderTabs('wizard'),
|
||||
this.renderHeader(),
|
||||
this.renderFirstRunCard(),
|
||||
this.renderProfilesCard(),
|
||||
this.renderAppsCard()
|
||||
]);
|
||||
return container;
|
||||
@ -142,6 +145,44 @@ return view.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
renderProfilesCard: function() {
|
||||
var profiles = this.profileList || [];
|
||||
return E('div', { 'class': 'sb-wizard-card' }, [
|
||||
E('div', { 'class': 'sb-wizard-title' }, ['🧱 ', _('Profiles')]),
|
||||
profiles.length ? E('div', { 'class': 'sb-app-grid' }, profiles.map(this.renderProfileCard, this)) :
|
||||
E('div', { 'class': 'secubox-empty-state' }, [
|
||||
E('div', { 'class': 'secubox-empty-icon' }, '📭'),
|
||||
E('div', { 'class': 'secubox-empty-title' }, _('No profiles available')),
|
||||
E('div', { 'class': 'secubox-empty-text' }, _('Profiles are stored in /usr/share/secubox/profiles/.'))
|
||||
]),
|
||||
profiles.length ? E('div', { 'class': 'right', 'style': 'margin-top:12px;' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'click': this.rollbackProfile.bind(this)
|
||||
}, _('Rollback last profile'))
|
||||
]) : ''
|
||||
]);
|
||||
},
|
||||
|
||||
renderProfileCard: function(profile) {
|
||||
var apps = profile.apps || [];
|
||||
return E('div', { 'class': 'sb-app-card' }, [
|
||||
E('div', { 'class': 'sb-app-card-info' }, [
|
||||
E('div', { 'class': 'sb-app-name' }, [profile.name || profile.id]),
|
||||
E('div', { 'class': 'sb-app-desc' }, profile.description || ''),
|
||||
E('div', { 'class': 'sb-app-desc' }, _('Network mode: %s').format(profile.network_mode || '—')),
|
||||
apps.length ? E('div', { 'class': 'sb-app-desc' }, _('Apps: %s').format(apps.join(', '))) : ''
|
||||
]),
|
||||
E('div', { 'class': 'sb-app-actions' }, [
|
||||
E('span', { 'class': 'sb-app-state' + (profile.state === 'installed' ? ' ok' : '') }, profile.state || 'n/a'),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'click': this.applyProfile.bind(this, profile.id)
|
||||
}, _('Apply'))
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
renderAppCard: function(app) {
|
||||
return E('div', { 'class': 'sb-app-card' }, [
|
||||
E('div', { 'class': 'sb-app-card-info' }, [
|
||||
@ -241,5 +282,31 @@ return view.extend({
|
||||
ui.addNotification(null, E('p', {}, _('Failed to apply wizard.')), 'error');
|
||||
}
|
||||
}).catch(this.showError);
|
||||
},
|
||||
|
||||
applyProfile: function(profileId) {
|
||||
if (!profileId)
|
||||
return;
|
||||
ui.showModal(_('Applying profile…'), [E('div', { 'class': 'spinning' })]);
|
||||
API.applyProfile(profileId).then(function(result) {
|
||||
ui.hideModal();
|
||||
if (result && result.success) {
|
||||
ui.addNotification(null, E('p', {}, _('Profile applied. A reboot may be required.')), 'info');
|
||||
} else {
|
||||
ui.addNotification(null, E('p', {}, (result && result.error) || _('Failed to apply profile')), 'error');
|
||||
}
|
||||
}).catch(this.showError);
|
||||
},
|
||||
|
||||
rollbackProfile: function() {
|
||||
ui.showModal(_('Rolling back…'), [E('div', { 'class': 'spinning' })]);
|
||||
API.rollbackProfile().then(function(result) {
|
||||
ui.hideModal();
|
||||
if (result && result.success) {
|
||||
ui.addNotification(null, E('p', {}, result.message || _('Rollback completed.')), 'info');
|
||||
} else {
|
||||
ui.addNotification(null, E('p', {}, (result && result.error) || _('Rollback failed.')), 'error');
|
||||
}
|
||||
}).catch(this.showError);
|
||||
}
|
||||
});
|
||||
|
||||
@ -55,7 +55,11 @@ get_pkg_version() {
|
||||
PKG_VERSION="$(get_pkg_version)"
|
||||
|
||||
PLUGIN_DIR="/usr/share/secubox/plugins"
|
||||
PROFILE_DIR="/usr/share/secubox/profiles"
|
||||
PROFILE_BACKUP_DIR="/etc/secubox-profiles/backups"
|
||||
DEFAULT_STORAGE_PATH="/srv/secubox"
|
||||
SECOBOX_APP="/usr/sbin/secubox-app"
|
||||
OPKG_UPDATED=0
|
||||
|
||||
# Module registry - auto-detected from /usr/libexec/rpcd/
|
||||
detect_modules() {
|
||||
@ -127,6 +131,34 @@ ensure_directory() {
|
||||
[ -d "$dir" ] || mkdir -p "$dir"
|
||||
}
|
||||
|
||||
apply_network_mode() {
|
||||
local mode="$1"
|
||||
[ -n "$mode" ] || return
|
||||
if command -v ubus >/dev/null 2>&1; then
|
||||
if ubus call luci.network-modes set_mode "{\"mode\":\"$mode\"}" >/dev/null 2>&1; then
|
||||
ubus call luci.network-modes apply_mode >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_opkg_updated() {
|
||||
[ "$OPKG_UPDATED" -eq 1 ] && return
|
||||
if command -v opkg >/dev/null 2>&1; then
|
||||
opkg update >/dev/null 2>&1 && OPKG_UPDATED=1
|
||||
fi
|
||||
}
|
||||
|
||||
install_package_if_missing() {
|
||||
local pkg="$1"
|
||||
package_installed "$pkg" && return
|
||||
if command -v opkg >/dev/null 2>&1; then
|
||||
ensure_opkg_updated
|
||||
opkg install "$pkg" >/dev/null 2>&1
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
apk add "$pkg" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if a module is installed (supports both opkg and apk)
|
||||
check_module_installed() {
|
||||
local module="$1"
|
||||
@ -1324,6 +1356,136 @@ apply_app_wizard() {
|
||||
json_add_boolean "success" 1
|
||||
json_dump
|
||||
}
|
||||
|
||||
list_profiles() {
|
||||
ensure_directory "$PROFILE_DIR"
|
||||
json_init
|
||||
json_add_array "profiles"
|
||||
local profile_file
|
||||
for profile_file in "$PROFILE_DIR"/*.json; do
|
||||
[ -f "$profile_file" ] || continue
|
||||
local profile_json id name description network_mode apps state
|
||||
profile_json=$(cat "$profile_file")
|
||||
id=$(jsonfilter -s "$profile_json" -e '@.id' 2>/dev/null)
|
||||
name=$(jsonfilter -s "$profile_json" -e '@.name' 2>/dev/null)
|
||||
description=$(jsonfilter -s "$profile_json" -e '@.description' 2>/dev/null)
|
||||
network_mode=$(jsonfilter -s "$profile_json" -e '@.network_mode' 2>/dev/null)
|
||||
apps=$(jsonfilter -s "$profile_json" -e '@.apps[*]' 2>/dev/null)
|
||||
state=$(packages_state "$profile_json")
|
||||
[ -n "$id" ] || continue
|
||||
json_add_object
|
||||
json_add_string "id" "$id"
|
||||
json_add_string "name" "$name"
|
||||
json_add_string "description" "$description"
|
||||
json_add_string "network_mode" "$network_mode"
|
||||
json_add_string "state" "$state"
|
||||
json_add_array "apps"
|
||||
for app in $apps; do
|
||||
json_add_string "" "$app"
|
||||
done
|
||||
json_close_array
|
||||
json_close_object
|
||||
done
|
||||
json_close_array
|
||||
json_dump
|
||||
}
|
||||
|
||||
apply_profile() {
|
||||
local input profile_id profile_file profile_json network_mode packages apps backup_file notes=""
|
||||
read input
|
||||
json_load "$input"
|
||||
json_get_var profile_id profile_id
|
||||
json_cleanup
|
||||
[ -n "$profile_id" ] || {
|
||||
json_init; json_add_boolean "success" 0; json_add_string "error" "profile_id required"; json_dump; return;
|
||||
}
|
||||
profile_file="$PROFILE_DIR/$profile_id.json"
|
||||
if [ ! -f "$profile_file" ]; then
|
||||
json_init; json_add_boolean "success" 0; json_add_string "error" "Profile not found"; json_dump; return;
|
||||
fi
|
||||
profile_json=$(cat "$profile_file")
|
||||
ensure_directory "$PROFILE_BACKUP_DIR"
|
||||
local timestamp=$(date +%Y%m%d_%H%M%S)
|
||||
backup_file="$PROFILE_BACKUP_DIR/profile_${profile_id}_${timestamp}.tar.gz"
|
||||
tar -czf "$backup_file" /etc/config 2>/dev/null
|
||||
notes="${notes}Backup saved to $backup_file\n"
|
||||
uci set secubox.profile.last_backup="$backup_file"
|
||||
uci set secubox.profile.last_profile="$profile_id"
|
||||
uci commit secubox
|
||||
network_mode=$(jsonfilter -s "$profile_json" -e '@.network_mode' 2>/dev/null)
|
||||
if [ -n "$network_mode" ]; then
|
||||
notes="${notes}Switched network mode to $network_mode\n"
|
||||
apply_network_mode "$network_mode"
|
||||
fi
|
||||
packages=$(jsonfilter -s "$profile_json" -e '@.packages[*]' 2>/dev/null)
|
||||
for pkg in $packages; do
|
||||
install_package_if_missing "$pkg"
|
||||
notes="${notes}Ensured package $pkg\n"
|
||||
done
|
||||
apps=$(jsonfilter -s "$profile_json" -e '@.apps[*]' 2>/dev/null)
|
||||
for app in $apps; do
|
||||
if [ -x "$SECOBOX_APP" ]; then
|
||||
$SECOBOX_APP install "$app" >/dev/null 2>&1 && notes="${notes}Installed app $app\n"
|
||||
fi
|
||||
done
|
||||
json_load "$profile_json"
|
||||
local uci_configs=""
|
||||
if json_select uci >/dev/null 2>&1; then
|
||||
local entries
|
||||
json_get_keys entries
|
||||
for entry in $entries; do
|
||||
json_select "$entry"
|
||||
local cfg section option value
|
||||
json_get_var cfg config
|
||||
json_get_var section section
|
||||
json_get_var option option
|
||||
json_get_var value value
|
||||
section=${section:-main}
|
||||
if [ -n "$cfg" ] && [ -n "$option" ]; then
|
||||
uci set ${cfg}.${section}.${option}="$value"
|
||||
uci_configs="${uci_configs} $cfg"
|
||||
notes="${notes}Set ${cfg}.${section}.${option}=${value}\n"
|
||||
fi
|
||||
json_select ..
|
||||
done
|
||||
json_select ..
|
||||
fi
|
||||
json_cleanup
|
||||
local committed=""
|
||||
for cfg in $uci_configs; do
|
||||
case " $committed " in
|
||||
*" $cfg "*) continue;;
|
||||
esac
|
||||
uci commit "$cfg"
|
||||
committed="$committed $cfg"
|
||||
done
|
||||
json_init
|
||||
json_add_boolean "success" 1
|
||||
json_add_array "messages"
|
||||
printf '%b' "$notes" | while IFS= read -r line; do
|
||||
[ -n "$line" ] && json_add_string "" "$line"
|
||||
done
|
||||
json_close_array
|
||||
json_add_string "backup" "$backup_file"
|
||||
json_dump
|
||||
}
|
||||
|
||||
rollback_profile() {
|
||||
local latest_backup
|
||||
latest_backup=$(ls -t "$PROFILE_BACKUP_DIR"/profile_*.tar.gz 2>/dev/null | head -1)
|
||||
if [ -z "$latest_backup" ]; then
|
||||
json_init; json_add_boolean "success" 0; json_add_string "error" "No backups available"; json_dump; return;
|
||||
fi
|
||||
cd /
|
||||
tar -xzf "$latest_backup" 2>/dev/null
|
||||
/etc/init.d/network reload >/dev/null 2>&1
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1
|
||||
/etc/init.d/dnsmasq reload >/dev/null 2>&1
|
||||
json_init
|
||||
json_add_boolean "success" 1
|
||||
json_add_string "message" "Restored $latest_backup"
|
||||
json_dump
|
||||
}
|
||||
case "$1" in
|
||||
list)
|
||||
json_init
|
||||
@ -1392,6 +1554,25 @@ case "$1" in
|
||||
json_add_object "apply_app_wizard"
|
||||
json_add_string "app_id" "string"
|
||||
json_close_object
|
||||
json_add_object "list_profiles"
|
||||
json_close_object
|
||||
json_add_object "apply_profile"
|
||||
json_add_string "profile_id" "string"
|
||||
json_close_object
|
||||
json_add_object "rollback_profile"
|
||||
json_close_object
|
||||
json_add_object "first_run_status"
|
||||
json_close_object
|
||||
json_add_object "apply_first_run"
|
||||
json_close_object
|
||||
json_add_object "list_apps"
|
||||
json_close_object
|
||||
json_add_object "get_app_manifest"
|
||||
json_add_string "app_id" "string"
|
||||
json_close_object
|
||||
json_add_object "apply_app_wizard"
|
||||
json_add_string "app_id" "string"
|
||||
json_close_object
|
||||
json_dump
|
||||
;;
|
||||
call)
|
||||
@ -1507,6 +1688,15 @@ case "$1" in
|
||||
apply_app_wizard)
|
||||
apply_app_wizard
|
||||
;;
|
||||
list_profiles)
|
||||
list_profiles
|
||||
;;
|
||||
apply_profile)
|
||||
apply_profile
|
||||
;;
|
||||
rollback_profile)
|
||||
rollback_profile
|
||||
;;
|
||||
*)
|
||||
echo '{"error":"Unknown method"}'
|
||||
;;
|
||||
|
||||
@ -17,7 +17,8 @@
|
||||
"get_theme",
|
||||
"first_run_status",
|
||||
"list_apps",
|
||||
"get_app_manifest"
|
||||
"get_app_manifest",
|
||||
"list_profiles"
|
||||
],
|
||||
"uci": [
|
||||
"get",
|
||||
@ -42,7 +43,9 @@
|
||||
"clear_alerts",
|
||||
"fix_permissions",
|
||||
"apply_first_run",
|
||||
"apply_app_wizard"
|
||||
"apply_app_wizard",
|
||||
"apply_profile",
|
||||
"rollback_profile"
|
||||
],
|
||||
"uci": [
|
||||
"set",
|
||||
|
||||
11
profiles/gateway_dmz.json
Normal file
11
profiles/gateway_dmz.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"id": "gateway_dmz",
|
||||
"name": "Gateway + DMZ",
|
||||
"description": "Router with isolated DMZ segment for exposed services.",
|
||||
"network_mode": "dmz",
|
||||
"apps": ["zigbee2mqtt"],
|
||||
"packages": ["luci-app-vhost-manager"],
|
||||
"uci": [
|
||||
{ "config": "secubox", "section": "vhost_manager", "option": "enabled", "value": "1" }
|
||||
]
|
||||
}
|
||||
12
profiles/hardened.json
Normal file
12
profiles/hardened.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "hardened",
|
||||
"name": "Hardened",
|
||||
"description": "Security-focused preset enabling CrowdSec and Client Guardian.",
|
||||
"network_mode": "router",
|
||||
"apps": [],
|
||||
"packages": ["luci-app-crowdsec-dashboard", "luci-app-client-guardian"],
|
||||
"uci": [
|
||||
{ "config": "secubox", "section": "crowdsec", "option": "enabled", "value": "1" },
|
||||
{ "config": "secubox", "section": "client_guardian", "option": "enabled", "value": "1" }
|
||||
]
|
||||
}
|
||||
12
profiles/home.json
Normal file
12
profiles/home.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "home",
|
||||
"name": "Home",
|
||||
"description": "Default home router with Zigbee and monitoring enabled.",
|
||||
"network_mode": "router",
|
||||
"apps": ["zigbee2mqtt"],
|
||||
"packages": ["luci-app-netdata-dashboard"],
|
||||
"uci": [
|
||||
{ "config": "secubox", "section": "netdata", "option": "enabled", "value": "1" },
|
||||
{ "config": "secubox", "section": "zigbee2mqtt", "option": "enabled", "value": "1" }
|
||||
]
|
||||
}
|
||||
12
profiles/lab.json
Normal file
12
profiles/lab.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "lab",
|
||||
"name": "Lab",
|
||||
"description": "Extended monitoring and DPI for test environments.",
|
||||
"network_mode": "router",
|
||||
"apps": [],
|
||||
"packages": ["luci-app-netifyd-dashboard", "luci-app-netdata-dashboard"],
|
||||
"uci": [
|
||||
{ "config": "secubox", "section": "netifyd", "option": "enabled", "value": "1" },
|
||||
{ "config": "secubox", "section": "bandwidth_manager", "option": "enabled", "value": "1" }
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user