From 170f4a90e2194ee42ed85f992a1aea2e892cbb34 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sat, 24 Jan 2026 13:00:05 +0100 Subject: [PATCH] feat(luci-hexojs): Add Gitea integration to sync page - Add gitea_status, gitea_sync, gitea_clone, gitea_save_config RPCD methods - Add Gitea section to sync.js with config form and sync buttons - Update ACL for new Gitea methods - Fix luci-app-metabolizer install section for RPCD executable Co-Authored-By: Claude Opus 4.5 --- package/secubox/luci-app-hexojs/Makefile | 2 +- .../luci-static/resources/view/hexojs/sync.js | 233 +++++++++++++++++- .../root/usr/libexec/rpcd/luci.hexojs | 133 +++++++++- .../usr/share/rpcd/acl.d/luci-app-hexojs.json | 9 +- 4 files changed, 367 insertions(+), 10 deletions(-) diff --git a/package/secubox/luci-app-hexojs/Makefile b/package/secubox/luci-app-hexojs/Makefile index 60459dfc..38cd31b2 100644 --- a/package/secubox/luci-app-hexojs/Makefile +++ b/package/secubox/luci-app-hexojs/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-hexojs PKG_VERSION:=1.0.0 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_ARCH:=all PKG_LICENSE:=MIT diff --git a/package/secubox/luci-app-hexojs/htdocs/luci-static/resources/view/hexojs/sync.js b/package/secubox/luci-app-hexojs/htdocs/luci-static/resources/view/hexojs/sync.js index bc898831..c433e2d6 100644 --- a/package/secubox/luci-app-hexojs/htdocs/luci-static/resources/view/hexojs/sync.js +++ b/package/secubox/luci-app-hexojs/htdocs/luci-static/resources/view/hexojs/sync.js @@ -1,14 +1,42 @@ 'use strict'; 'require view'; 'require ui'; +'require rpc'; 'require hexojs/api as api'; +var callGiteaStatus = rpc.declare({ + object: 'luci.hexojs', + method: 'gitea_status', + expect: {} +}); + +var callGiteaSync = rpc.declare({ + object: 'luci.hexojs', + method: 'gitea_sync', + expect: {} +}); + +var callGiteaClone = rpc.declare({ + object: 'luci.hexojs', + method: 'gitea_clone', + expect: {} +}); + +var callGiteaSaveConfig = rpc.declare({ + object: 'luci.hexojs', + method: 'gitea_save_config', + params: ['enabled', 'gitea_url', 'gitea_user', 'gitea_token', 'content_repo', 'content_branch', 'auto_sync'] +}); + return view.extend({ - title: _('GitHub Sync'), + title: _('Content Sync'), wizardStep: 0, load: function() { - return api.getGitSyncData(); + return Promise.all([ + api.getGitSyncData(), + callGiteaStatus() + ]); }, // ============================================ @@ -182,15 +210,76 @@ return view.extend({ }); }, + // ============================================ + // Gitea Actions + // ============================================ + + handleGiteaSync: function() { + ui.showModal(_('Syncing from Gitea'), [ + E('p', { 'class': 'spinning' }, _('Pulling content from Gitea...')) + ]); + + callGiteaSync().then(function(result) { + ui.hideModal(); + if (result.success) { + ui.addNotification(null, E('p', _('Content synced from Gitea!')), 'info'); + location.reload(); + } else { + ui.addNotification(null, E('p', result.error || _('Sync failed')), 'error'); + } + }).catch(function(err) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: %s').format(err.message || err)), 'error'); + }); + }, + + handleGiteaClone: function() { + ui.showModal(_('Cloning from Gitea'), [ + E('p', { 'class': 'spinning' }, _('Cloning content repository from Gitea...')) + ]); + + callGiteaClone().then(function(result) { + ui.hideModal(); + if (result.success) { + ui.addNotification(null, E('p', _('Content cloned from Gitea!')), 'info'); + location.reload(); + } else { + ui.addNotification(null, E('p', result.error || _('Clone failed')), 'error'); + } + }).catch(function(err) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Error: %s').format(err.message || err)), 'error'); + }); + }, + + handleGiteaSaveConfig: function() { + var enabled = document.querySelector('#gitea-enabled').checked ? '1' : '0'; + var url = document.querySelector('#gitea-url').value; + var user = document.querySelector('#gitea-user').value; + var token = document.querySelector('#gitea-token').value; + var repo = document.querySelector('#gitea-repo').value; + var branch = document.querySelector('#gitea-branch').value || 'main'; + + callGiteaSaveConfig(enabled, url, user, token, repo, branch, '0').then(function(result) { + if (result.success) { + ui.addNotification(null, E('p', _('Gitea configuration saved!')), 'info'); + } else { + ui.addNotification(null, E('p', result.error || _('Save failed')), 'error'); + } + }); + }, + // ============================================ // Render // ============================================ render: function(data) { var self = this; - var status = data.status || {}; - var credentials = data.credentials || {}; - var commits = data.commits || []; + var gitData = data[0] || {}; + var giteaStatus = data[1] || {}; + var status = gitData.status || {}; + var credentials = gitData.credentials || {}; + var commits = gitData.commits || []; var isRepo = status.is_repo; return E('div', { 'class': 'hexo-dashboard' }, [ @@ -200,7 +289,7 @@ return view.extend({ E('div', { 'class': 'hexo-header' }, [ E('div', { 'class': 'hexo-card-title' }, [ E('span', { 'class': 'hexo-card-title-icon' }, '\uD83D\uDD04'), - _('GitHub Sync') + _('Content Sync') ]), isRepo ? E('div', { 'class': 'hexo-status-badge ' + (status.ahead > 0 ? 'warning' : 'running') }, [ E('span', { 'class': 'hexo-status-dot' }), @@ -208,6 +297,138 @@ return view.extend({ ]) : '' ]), + // Gitea Integration Card + E('div', { 'class': 'hexo-card' }, [ + E('div', { 'class': 'hexo-card-header' }, [ + E('div', { 'class': 'hexo-card-title' }, [ + E('span', { 'style': 'margin-right: 8px;' }, '\uD83E\uDD8A'), + _('Gitea Content Sync') + ]), + giteaStatus.enabled ? E('div', { 'class': 'hexo-status-badge running' }, [ + E('span', { 'class': 'hexo-status-dot' }), + _('Enabled') + ]) : E('div', { 'class': 'hexo-status-badge stopped' }, [ + E('span', { 'class': 'hexo-status-dot' }), + _('Disabled') + ]) + ]), + E('p', { 'style': 'margin-bottom: 16px; color: var(--hexo-text-muted);' }, + _('Sync blog content from your local Gitea server.')), + + // Gitea Status + giteaStatus.has_local_repo ? E('div', { 'style': 'background: var(--hexo-bg-input); padding: 12px; border-radius: 8px; margin-bottom: 16px;' }, [ + E('div', { 'style': 'display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px;' }, [ + E('div', {}, [ + E('div', { 'style': 'font-size: 11px; color: var(--hexo-text-muted); text-transform: uppercase;' }, _('Repository')), + E('div', { 'style': 'font-size: 14px;' }, giteaStatus.content_repo || '-') + ]), + E('div', {}, [ + E('div', { 'style': 'font-size: 11px; color: var(--hexo-text-muted); text-transform: uppercase;' }, _('Branch')), + E('div', { 'style': 'font-size: 14px;' }, giteaStatus.local_branch || '-') + ]), + E('div', {}, [ + E('div', { 'style': 'font-size: 11px; color: var(--hexo-text-muted); text-transform: uppercase;' }, _('Last Commit')), + E('div', { 'style': 'font-size: 13px;' }, giteaStatus.last_commit || '-') + ]) + ]) + ]) : '', + + // Gitea Actions + E('div', { 'style': 'display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px;' }, [ + giteaStatus.has_local_repo ? + E('button', { + 'class': 'hexo-btn hexo-btn-primary', + 'click': function() { self.handleGiteaSync(); }, + 'disabled': !giteaStatus.enabled + }, ['\uD83D\uDD04 ', _('Sync from Gitea')]) : + E('button', { + 'class': 'hexo-btn hexo-btn-success', + 'click': function() { self.handleGiteaClone(); }, + 'disabled': !giteaStatus.enabled + }, ['\uD83D\uDCE5 ', _('Clone from Gitea')]) + ]), + + // Gitea Config Form + E('details', { 'style': 'margin-top: 8px;' }, [ + E('summary', { 'style': 'cursor: pointer; color: var(--hexo-primary);' }, _('Configure Gitea Connection')), + E('div', { 'style': 'margin-top: 12px; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;' }, [ + E('div', { 'class': 'hexo-form-group', 'style': 'margin: 0;' }, [ + E('label', { 'style': 'display: flex; align-items: center; gap: 8px;' }, [ + E('input', { + 'type': 'checkbox', + 'id': 'gitea-enabled', + 'checked': giteaStatus.enabled + }), + _('Enable Gitea Sync') + ]) + ]), + E('div', { 'class': 'hexo-form-group', 'style': 'margin: 0;' }, [ + E('label', { 'class': 'hexo-form-label' }, _('Gitea URL')), + E('input', { + 'type': 'text', + 'id': 'gitea-url', + 'class': 'hexo-input', + 'value': giteaStatus.gitea_url || '', + 'placeholder': 'http://192.168.255.1:3000' + }) + ]), + E('div', { 'class': 'hexo-form-group', 'style': 'margin: 0;' }, [ + E('label', { 'class': 'hexo-form-label' }, _('Username')), + E('input', { + 'type': 'text', + 'id': 'gitea-user', + 'class': 'hexo-input', + 'value': giteaStatus.gitea_user || '', + 'placeholder': 'admin' + }) + ]), + E('div', { 'class': 'hexo-form-group', 'style': 'margin: 0;' }, [ + E('label', { 'class': 'hexo-form-label' }, _('Access Token')), + E('input', { + 'type': 'password', + 'id': 'gitea-token', + 'class': 'hexo-input', + 'placeholder': _('Gitea access token') + }) + ]), + E('div', { 'class': 'hexo-form-group', 'style': 'margin: 0;' }, [ + E('label', { 'class': 'hexo-form-label' }, _('Content Repository')), + E('input', { + 'type': 'text', + 'id': 'gitea-repo', + 'class': 'hexo-input', + 'value': giteaStatus.content_repo || '', + 'placeholder': 'blog-content' + }) + ]), + E('div', { 'class': 'hexo-form-group', 'style': 'margin: 0;' }, [ + E('label', { 'class': 'hexo-form-label' }, _('Branch')), + E('input', { + 'type': 'text', + 'id': 'gitea-branch', + 'class': 'hexo-input', + 'value': giteaStatus.content_branch || 'main', + 'placeholder': 'main' + }) + ]) + ]), + E('button', { + 'class': 'hexo-btn hexo-btn-secondary', + 'style': 'margin-top: 12px;', + 'click': function() { self.handleGiteaSaveConfig(); } + }, _('Save Gitea Config')) + ]) + ]), + + // Divider + E('div', { 'style': 'border-top: 1px solid var(--hexo-border); margin: 24px 0;' }), + + // GitHub Sync Header + E('div', { 'class': 'hexo-card-title', 'style': 'margin-bottom: 16px;' }, [ + E('span', { 'class': 'hexo-card-title-icon' }, '\uD83D\uDC19'), + _('GitHub / Git Sync') + ]), + // Setup Wizard (shown if not a repo) !isRepo ? E('div', { 'class': 'hexo-card' }, [ E('div', { 'class': 'hexo-card-header' }, [ diff --git a/package/secubox/luci-app-hexojs/root/usr/libexec/rpcd/luci.hexojs b/package/secubox/luci-app-hexojs/root/usr/libexec/rpcd/luci.hexojs index 4445905e..d945218d 100644 --- a/package/secubox/luci-app-hexojs/root/usr/libexec/rpcd/luci.hexojs +++ b/package/secubox/luci-app-hexojs/root/usr/libexec/rpcd/luci.hexojs @@ -1423,6 +1423,127 @@ git_get_credentials() { json_dump } +# ============================================ +# Gitea Integration Methods +# ============================================ + +gitea_status() { + json_init + + local enabled=$(uci -q get hexojs.gitea.enabled) || enabled="0" + local gitea_url=$(uci -q get hexojs.gitea.url) || gitea_url="" + local gitea_user=$(uci -q get hexojs.gitea.user) || gitea_user="" + local content_repo=$(uci -q get hexojs.gitea.content_repo) || content_repo="" + local content_branch=$(uci -q get hexojs.gitea.content_branch) || content_branch="main" + local auto_sync=$(uci -q get hexojs.gitea.auto_sync) || auto_sync="0" + + local data_path=$(uci_get main.data_path) || data_path="$DATA_PATH" + local content_path="$data_path/content" + local has_repo="false" + local last_commit="" + local branch="" + + if [ -d "$content_path/.git" ]; then + has_repo="true" + cd "$content_path" + last_commit=$(git log -1 --format="%h %s" 2>/dev/null || echo "unknown") + branch=$(git branch --show-current 2>/dev/null || echo "unknown") + fi + + json_add_boolean "enabled" "$enabled" + json_add_string "gitea_url" "$gitea_url" + json_add_string "gitea_user" "$gitea_user" + json_add_string "content_repo" "$content_repo" + json_add_string "content_branch" "$content_branch" + json_add_boolean "auto_sync" "$auto_sync" + json_add_boolean "has_local_repo" "$([ "$has_repo" = "true" ] && echo 1 || echo 0)" + json_add_string "local_branch" "$branch" + json_add_string "last_commit" "$last_commit" + + json_dump +} + +gitea_setup() { + json_init + + local output=$("$HEXOCTL" gitea setup 2>&1) + local result=$? + + if [ "$result" -eq 0 ]; then + json_add_boolean "success" 1 + json_add_string "message" "Git credentials configured" + else + json_add_boolean "success" 0 + json_add_string "error" "$output" + fi + + json_dump +} + +gitea_clone() { + json_init + + local output=$("$HEXOCTL" gitea clone 2>&1) + local result=$? + + if [ "$result" -eq 0 ]; then + json_add_boolean "success" 1 + json_add_string "message" "Content cloned from Gitea" + else + json_add_boolean "success" 0 + json_add_string "error" "$output" + fi + + json_dump +} + +gitea_sync() { + json_init + + local output=$("$HEXOCTL" gitea sync 2>&1) + local result=$? + + if [ "$result" -eq 0 ]; then + json_add_boolean "success" 1 + json_add_string "message" "Content synced from Gitea" + else + json_add_boolean "success" 0 + json_add_string "error" "$output" + fi + + json_dump +} + +gitea_save_config() { + read input + json_load "$input" + + json_get_var enabled enabled + json_get_var gitea_url gitea_url + json_get_var gitea_user gitea_user + json_get_var gitea_token gitea_token + json_get_var content_repo content_repo + json_get_var content_branch content_branch + json_get_var auto_sync auto_sync + + json_init + + [ -n "$enabled" ] && uci set hexojs.gitea.enabled="$enabled" + [ -n "$gitea_url" ] && uci set hexojs.gitea.url="$gitea_url" + [ -n "$gitea_user" ] && uci set hexojs.gitea.user="$gitea_user" + [ -n "$gitea_token" ] && uci set hexojs.gitea.token="$gitea_token" + [ -n "$content_repo" ] && uci set hexojs.gitea.content_repo="$content_repo" + [ -n "$content_branch" ] && uci set hexojs.gitea.content_branch="$content_branch" + [ -n "$auto_sync" ] && uci set hexojs.gitea.auto_sync="$auto_sync" + + uci commit hexojs + + json_add_boolean "success" 1 + json_add_string "message" "Gitea configuration saved" + + json_dump +} + # ============================================ # Service Control # ============================================ @@ -1508,7 +1629,12 @@ case "$1" in "git_log": {}, "git_reset": {"hard": "bool"}, "git_set_credentials": {"name": "str", "email": "str"}, - "git_get_credentials": {} + "git_get_credentials": {}, + "gitea_status": {}, + "gitea_setup": {}, + "gitea_clone": {}, + "gitea_sync": {}, + "gitea_save_config": {"enabled": "bool", "gitea_url": "str", "gitea_user": "str", "gitea_token": "str", "content_repo": "str", "content_branch": "str", "auto_sync": "bool"} } EOF ;; @@ -1555,6 +1681,11 @@ EOF git_reset) git_reset ;; git_set_credentials) git_set_credentials ;; git_get_credentials) git_get_credentials ;; + gitea_status) gitea_status ;; + gitea_setup) gitea_setup ;; + gitea_clone) gitea_clone ;; + gitea_sync) gitea_sync ;; + gitea_save_config) gitea_save_config ;; *) echo '{"error": "Unknown method"}' ;; esac ;; diff --git a/package/secubox/luci-app-hexojs/root/usr/share/rpcd/acl.d/luci-app-hexojs.json b/package/secubox/luci-app-hexojs/root/usr/share/rpcd/acl.d/luci-app-hexojs.json index 107b2e60..2104e057 100644 --- a/package/secubox/luci-app-hexojs/root/usr/share/rpcd/acl.d/luci-app-hexojs.json +++ b/package/secubox/luci-app-hexojs/root/usr/share/rpcd/acl.d/luci-app-hexojs.json @@ -21,7 +21,8 @@ "list_presets", "git_status", "git_log", - "git_get_credentials" + "git_get_credentials", + "gitea_status" ] }, "uci": ["hexojs"] @@ -51,7 +52,11 @@ "git_push", "git_fetch", "git_reset", - "git_set_credentials" + "git_set_credentials", + "gitea_setup", + "gitea_clone", + "gitea_sync", + "gitea_save_config" ] }, "uci": ["hexojs"]