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 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-24 13:00:05 +01:00
parent 70358370f6
commit 170f4a90e2
4 changed files with 367 additions and 10 deletions

View File

@ -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

View File

@ -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' }, [

View File

@ -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
;;

View File

@ -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"]