- Add publish_to_www RPCD method to publish static files to /www/blog - Add Build & Publish card in sync.js with configurable publish path - Add generate RPC call for building site - Fix file permissions for all RPCD scripts and init.d scripts - Bump luci-app-hexojs to 1.0.0-r3 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1734 lines
41 KiB
Bash
Executable File
1734 lines
41 KiB
Bash
Executable File
#!/bin/sh
|
|
# SPDX-License-Identifier: MIT
|
|
# Hexo CMS RPCD backend
|
|
# Copyright (C) 2025 CyberMind.fr
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
CONFIG="hexojs"
|
|
HEXOCTL="/usr/sbin/hexoctl"
|
|
DATA_PATH="/srv/hexojs"
|
|
|
|
# Helper functions
|
|
uci_get() { uci -q get ${CONFIG}.$1; }
|
|
uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; }
|
|
|
|
is_running() {
|
|
pgrep -f "lxc.*hexojs" >/dev/null 2>&1
|
|
}
|
|
|
|
get_site_path() {
|
|
local data_path=$(uci_get main.data_path) || data_path="$DATA_PATH"
|
|
echo "$data_path/site"
|
|
}
|
|
|
|
# ============================================
|
|
# Status Methods
|
|
# ============================================
|
|
|
|
get_status() {
|
|
local data_path=$(uci_get main.data_path) || data_path="$DATA_PATH"
|
|
local site_path="$data_path/site"
|
|
local enabled=$(uci_get main.enabled) || enabled="0"
|
|
local http_port=$(uci_get main.http_port) || http_port="4000"
|
|
local active_site=$(uci_get main.active_site) || active_site="default"
|
|
|
|
json_init
|
|
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_add_boolean "running" "$(is_running && echo 1 || echo 0)"
|
|
json_add_int "http_port" "$http_port"
|
|
json_add_string "active_site" "$active_site"
|
|
json_add_string "data_path" "$data_path"
|
|
|
|
# Site info
|
|
if [ -d "$site_path" ]; then
|
|
json_add_boolean "site_exists" 1
|
|
|
|
local post_count=0
|
|
local draft_count=0
|
|
local page_count=0
|
|
|
|
[ -d "$site_path/source/_posts" ] && post_count=$(ls -1 "$site_path/source/_posts/"*.md 2>/dev/null | wc -l)
|
|
[ -d "$site_path/source/_drafts" ] && draft_count=$(ls -1 "$site_path/source/_drafts/"*.md 2>/dev/null | wc -l)
|
|
|
|
json_add_int "post_count" "$post_count"
|
|
json_add_int "draft_count" "$draft_count"
|
|
|
|
# Site config
|
|
local title=$(uci_get ${active_site}.title) || title="Blog"
|
|
local author=$(uci_get ${active_site}.author) || author="Admin"
|
|
local theme=$(uci_get ${active_site}.theme) || theme="cybermind"
|
|
|
|
json_add_object "site"
|
|
json_add_string "title" "$title"
|
|
json_add_string "author" "$author"
|
|
json_add_string "theme" "$theme"
|
|
json_close_object
|
|
else
|
|
json_add_boolean "site_exists" 0
|
|
json_add_int "post_count" 0
|
|
json_add_int "draft_count" 0
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
get_site_stats() {
|
|
local site_path=$(get_site_path)
|
|
|
|
json_init
|
|
|
|
if [ ! -d "$site_path" ]; then
|
|
json_add_int "posts" 0
|
|
json_add_int "drafts" 0
|
|
json_add_int "pages" 0
|
|
json_add_int "categories" 0
|
|
json_add_int "tags" 0
|
|
json_add_int "media" 0
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local post_count=0
|
|
local draft_count=0
|
|
local page_count=0
|
|
local media_count=0
|
|
|
|
[ -d "$site_path/source/_posts" ] && post_count=$(ls -1 "$site_path/source/_posts/"*.md 2>/dev/null | wc -l)
|
|
[ -d "$site_path/source/_drafts" ] && draft_count=$(ls -1 "$site_path/source/_drafts/"*.md 2>/dev/null | wc -l)
|
|
[ -d "$site_path/source/images" ] && media_count=$(find "$site_path/source/images" -type f 2>/dev/null | wc -l)
|
|
|
|
# Count unique categories and tags from posts
|
|
local categories=""
|
|
local tags=""
|
|
|
|
if [ -d "$site_path/source/_posts" ]; then
|
|
for f in "$site_path/source/_posts/"*.md; do
|
|
[ -f "$f" ] || continue
|
|
local cat=$(grep -m1 "^categories:" "$f" 2>/dev/null | sed 's/^categories:[[:space:]]*//' | tr -d '[]' | tr ',' '\n')
|
|
local tag=$(grep -m1 "^tags:" "$f" 2>/dev/null | sed 's/^tags:[[:space:]]*//' | tr -d '[]' | tr ',' '\n')
|
|
categories="$categories
|
|
$cat"
|
|
tags="$tags
|
|
$tag"
|
|
done
|
|
fi
|
|
|
|
local cat_count=$(echo "$categories" | grep -v '^$' | sort -u | wc -l)
|
|
local tag_count=$(echo "$tags" | grep -v '^$' | sort -u | wc -l)
|
|
|
|
json_add_int "posts" "$post_count"
|
|
json_add_int "drafts" "$draft_count"
|
|
json_add_int "pages" "$page_count"
|
|
json_add_int "categories" "$cat_count"
|
|
json_add_int "tags" "$tag_count"
|
|
json_add_int "media" "$media_count"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Post Methods
|
|
# ============================================
|
|
|
|
list_posts() {
|
|
local site_path=$(get_site_path)
|
|
local posts_dir="$site_path/source/_posts"
|
|
|
|
json_init
|
|
json_add_array "posts"
|
|
|
|
if [ -d "$posts_dir" ]; then
|
|
for f in "$posts_dir"/*.md; do
|
|
[ -f "$f" ] || continue
|
|
|
|
local filename=$(basename "$f")
|
|
local slug="${filename%.md}"
|
|
|
|
# Parse front matter
|
|
local title=$(grep -m1 "^title:" "$f" | sed 's/^title:[[:space:]]*//' | tr -d '"' | tr -d "'")
|
|
local date=$(grep -m1 "^date:" "$f" | sed 's/^date:[[:space:]]*//')
|
|
local categories=$(grep -m1 "^categories:" "$f" | sed 's/^categories:[[:space:]]*//' | tr -d '[]')
|
|
local tags=$(grep -m1 "^tags:" "$f" | sed 's/^tags:[[:space:]]*//' | tr -d '[]')
|
|
local excerpt=$(grep -m1 "^excerpt:" "$f" | sed 's/^excerpt:[[:space:]]*//' | tr -d '"')
|
|
|
|
json_add_object
|
|
json_add_string "slug" "$slug"
|
|
json_add_string "title" "${title:-$slug}"
|
|
json_add_string "date" "$date"
|
|
json_add_string "categories" "$categories"
|
|
json_add_string "tags" "$tags"
|
|
json_add_string "excerpt" "$excerpt"
|
|
json_add_string "path" "$f"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
get_post() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var slug slug
|
|
|
|
json_init
|
|
|
|
if [ -z "$slug" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Slug required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local site_path=$(get_site_path)
|
|
local post_file="$site_path/source/_posts/${slug}.md"
|
|
|
|
if [ ! -f "$post_file" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Post not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Parse front matter and content
|
|
local title=$(grep -m1 "^title:" "$post_file" | sed 's/^title:[[:space:]]*//' | tr -d '"' | tr -d "'")
|
|
local date=$(grep -m1 "^date:" "$post_file" | sed 's/^date:[[:space:]]*//')
|
|
local categories=$(grep -m1 "^categories:" "$post_file" | sed 's/^categories:[[:space:]]*//' | tr -d '[]')
|
|
local tags=$(grep -m1 "^tags:" "$post_file" | sed 's/^tags:[[:space:]]*//' | tr -d '[]')
|
|
local cover=$(grep -m1 "^cover:" "$post_file" | sed 's/^cover:[[:space:]]*//')
|
|
local excerpt=$(grep -m1 "^excerpt:" "$post_file" | sed 's/^excerpt:[[:space:]]*//' | tr -d '"')
|
|
|
|
# Get content after front matter
|
|
local content=$(awk '/^---$/,/^---$/{next} {print}' "$post_file" | tail -n +2)
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "slug" "$slug"
|
|
json_add_string "title" "$title"
|
|
json_add_string "date" "$date"
|
|
json_add_string "categories" "$categories"
|
|
json_add_string "tags" "$tags"
|
|
json_add_string "cover" "$cover"
|
|
json_add_string "excerpt" "$excerpt"
|
|
json_add_string "content" "$content"
|
|
json_add_string "path" "$post_file"
|
|
|
|
json_dump
|
|
}
|
|
|
|
create_post() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var title title
|
|
json_get_var content content
|
|
json_get_var categories categories
|
|
json_get_var tags tags
|
|
json_get_var excerpt excerpt
|
|
|
|
json_init
|
|
|
|
if [ -z "$title" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Title required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Generate slug from title
|
|
local slug=$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-')
|
|
local date=$(date "+%Y-%m-%d %H:%M:%S")
|
|
|
|
local site_path=$(get_site_path)
|
|
local posts_dir="$site_path/source/_posts"
|
|
local post_file="$posts_dir/${slug}.md"
|
|
|
|
[ -d "$posts_dir" ] || mkdir -p "$posts_dir"
|
|
|
|
# Check if file exists
|
|
if [ -f "$post_file" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Post with this slug already exists"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Create post file
|
|
cat > "$post_file" << EOF
|
|
---
|
|
title: $title
|
|
date: $date
|
|
categories: [$categories]
|
|
tags: [$tags]
|
|
excerpt: $excerpt
|
|
---
|
|
|
|
$content
|
|
EOF
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "slug" "$slug"
|
|
json_add_string "path" "$post_file"
|
|
json_add_string "message" "Post created"
|
|
|
|
json_dump
|
|
}
|
|
|
|
update_post() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var slug slug
|
|
json_get_var title title
|
|
json_get_var content content
|
|
json_get_var categories categories
|
|
json_get_var tags tags
|
|
json_get_var excerpt excerpt
|
|
json_get_var cover cover
|
|
|
|
json_init
|
|
|
|
if [ -z "$slug" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Slug required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local site_path=$(get_site_path)
|
|
local post_file="$site_path/source/_posts/${slug}.md"
|
|
|
|
if [ ! -f "$post_file" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Post not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Get original date
|
|
local date=$(grep -m1 "^date:" "$post_file" | sed 's/^date:[[:space:]]*//')
|
|
|
|
# Rewrite post file
|
|
cat > "$post_file" << EOF
|
|
---
|
|
title: $title
|
|
date: $date
|
|
categories: [$categories]
|
|
tags: [$tags]
|
|
cover: $cover
|
|
excerpt: $excerpt
|
|
---
|
|
|
|
$content
|
|
EOF
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Post updated"
|
|
|
|
json_dump
|
|
}
|
|
|
|
delete_post() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var slug slug
|
|
|
|
json_init
|
|
|
|
if [ -z "$slug" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Slug required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local site_path=$(get_site_path)
|
|
local post_file="$site_path/source/_posts/${slug}.md"
|
|
|
|
if [ ! -f "$post_file" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Post not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
rm -f "$post_file"
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Post deleted"
|
|
|
|
json_dump
|
|
}
|
|
|
|
publish_post() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var slug slug
|
|
|
|
json_init
|
|
|
|
if [ -z "$slug" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Slug required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local site_path=$(get_site_path)
|
|
local draft_file="$site_path/source/_drafts/${slug}.md"
|
|
local posts_dir="$site_path/source/_posts"
|
|
local post_file="$posts_dir/${slug}.md"
|
|
|
|
if [ ! -f "$draft_file" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Draft not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
[ -d "$posts_dir" ] || mkdir -p "$posts_dir"
|
|
|
|
# Add date if not present
|
|
if ! grep -q "^date:" "$draft_file"; then
|
|
local date=$(date "+%Y-%m-%d %H:%M:%S")
|
|
sed -i "/^title:/a date: $date" "$draft_file"
|
|
fi
|
|
|
|
mv "$draft_file" "$post_file"
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Draft published"
|
|
json_add_string "path" "$post_file"
|
|
|
|
json_dump
|
|
}
|
|
|
|
list_drafts() {
|
|
local site_path=$(get_site_path)
|
|
local drafts_dir="$site_path/source/_drafts"
|
|
|
|
json_init
|
|
json_add_array "drafts"
|
|
|
|
if [ -d "$drafts_dir" ]; then
|
|
for f in "$drafts_dir"/*.md; do
|
|
[ -f "$f" ] || continue
|
|
|
|
local filename=$(basename "$f")
|
|
local slug="${filename%.md}"
|
|
local title=$(grep -m1 "^title:" "$f" | sed 's/^title:[[:space:]]*//' | tr -d '"' | tr -d "'")
|
|
|
|
json_add_object
|
|
json_add_string "slug" "$slug"
|
|
json_add_string "title" "${title:-$slug}"
|
|
json_add_string "path" "$f"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
search_posts() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var query query
|
|
json_get_var category category
|
|
json_get_var tag tag
|
|
|
|
local site_path=$(get_site_path)
|
|
local posts_dir="$site_path/source/_posts"
|
|
|
|
json_init
|
|
json_add_array "posts"
|
|
|
|
if [ -d "$posts_dir" ]; then
|
|
for f in "$posts_dir"/*.md; do
|
|
[ -f "$f" ] || continue
|
|
|
|
local match=1
|
|
|
|
# Filter by query
|
|
if [ -n "$query" ]; then
|
|
grep -qi "$query" "$f" || match=0
|
|
fi
|
|
|
|
# Filter by category
|
|
if [ -n "$category" ] && [ "$match" = "1" ]; then
|
|
grep -qi "categories:.*$category" "$f" || match=0
|
|
fi
|
|
|
|
# Filter by tag
|
|
if [ -n "$tag" ] && [ "$match" = "1" ]; then
|
|
grep -qi "tags:.*$tag" "$f" || match=0
|
|
fi
|
|
|
|
if [ "$match" = "1" ]; then
|
|
local filename=$(basename "$f")
|
|
local slug="${filename%.md}"
|
|
local title=$(grep -m1 "^title:" "$f" | sed 's/^title:[[:space:]]*//' | tr -d '"' | tr -d "'")
|
|
local date=$(grep -m1 "^date:" "$f" | sed 's/^date:[[:space:]]*//')
|
|
|
|
json_add_object
|
|
json_add_string "slug" "$slug"
|
|
json_add_string "title" "${title:-$slug}"
|
|
json_add_string "date" "$date"
|
|
json_close_object
|
|
fi
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Taxonomy Methods
|
|
# ============================================
|
|
|
|
list_categories() {
|
|
local site_path=$(get_site_path)
|
|
local posts_dir="$site_path/source/_posts"
|
|
|
|
json_init
|
|
json_add_array "categories"
|
|
|
|
if [ -d "$posts_dir" ]; then
|
|
local cats=""
|
|
for f in "$posts_dir"/*.md; do
|
|
[ -f "$f" ] || continue
|
|
local cat=$(grep -m1 "^categories:" "$f" | sed 's/^categories:[[:space:]]*//' | tr -d '[]' | tr ',' '\n')
|
|
cats="$cats
|
|
$cat"
|
|
done
|
|
|
|
# Count unique categories
|
|
echo "$cats" | grep -v '^$' | sort | uniq -c | while read count name; do
|
|
name=$(echo "$name" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
|
|
[ -n "$name" ] || continue
|
|
json_add_object
|
|
json_add_string "name" "$name"
|
|
json_add_int "count" "$count"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
list_tags() {
|
|
local site_path=$(get_site_path)
|
|
local posts_dir="$site_path/source/_posts"
|
|
|
|
json_init
|
|
json_add_array "tags"
|
|
|
|
if [ -d "$posts_dir" ]; then
|
|
local tags=""
|
|
for f in "$posts_dir"/*.md; do
|
|
[ -f "$f" ] || continue
|
|
local tag=$(grep -m1 "^tags:" "$f" | sed 's/^tags:[[:space:]]*//' | tr -d '[]' | tr ',' '\n')
|
|
tags="$tags
|
|
$tag"
|
|
done
|
|
|
|
# Count unique tags
|
|
echo "$tags" | grep -v '^$' | sort | uniq -c | while read count name; do
|
|
name=$(echo "$name" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
|
|
[ -n "$name" ] || continue
|
|
json_add_object
|
|
json_add_string "name" "$name"
|
|
json_add_int "count" "$count"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Media Methods
|
|
# ============================================
|
|
|
|
list_media() {
|
|
local site_path=$(get_site_path)
|
|
local media_dir="$site_path/source/images"
|
|
|
|
json_init
|
|
json_add_array "media"
|
|
|
|
if [ -d "$media_dir" ]; then
|
|
find "$media_dir" -type f | while read f; do
|
|
local filename=$(basename "$f")
|
|
local size=$(stat -c %s "$f" 2>/dev/null || echo 0)
|
|
local mtime=$(stat -c %Y "$f" 2>/dev/null || echo 0)
|
|
local relpath="${f#$site_path/source}"
|
|
|
|
json_add_object
|
|
json_add_string "name" "$filename"
|
|
json_add_string "path" "$relpath"
|
|
json_add_string "full_path" "$f"
|
|
json_add_int "size" "$size"
|
|
json_add_int "mtime" "$mtime"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
delete_media() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var path path
|
|
|
|
json_init
|
|
|
|
if [ -z "$path" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Path required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local site_path=$(get_site_path)
|
|
local full_path="$site_path/source$path"
|
|
|
|
if [ ! -f "$full_path" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "File not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
rm -f "$full_path"
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Media deleted"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Apps (Portfolio) Methods
|
|
# ============================================
|
|
|
|
list_apps() {
|
|
local site_path=$(get_site_path)
|
|
local apps_dir="$site_path/source/apps"
|
|
|
|
json_init
|
|
json_add_array "apps"
|
|
|
|
if [ -d "$apps_dir" ]; then
|
|
for f in "$apps_dir"/*.md; do
|
|
[ -f "$f" ] || continue
|
|
|
|
local filename=$(basename "$f")
|
|
local slug="${filename%.md}"
|
|
local title=$(grep -m1 "^title:" "$f" | sed 's/^title:[[:space:]]*//' | tr -d '"' | tr -d "'")
|
|
local icon=$(grep -m1 "^icon:" "$f" | sed 's/^icon:[[:space:]]*//')
|
|
local description=$(grep -m1 "^description:" "$f" | sed 's/^description:[[:space:]]*//' | tr -d '"')
|
|
local url=$(grep -m1 "^url:" "$f" | sed 's/^url:[[:space:]]*//')
|
|
local category=$(grep -m1 "^category:" "$f" | sed 's/^category:[[:space:]]*//')
|
|
local featured=$(grep -m1 "^featured:" "$f" | sed 's/^featured:[[:space:]]*//')
|
|
|
|
json_add_object
|
|
json_add_string "slug" "$slug"
|
|
json_add_string "title" "${title:-$slug}"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "description" "$description"
|
|
json_add_string "url" "$url"
|
|
json_add_string "category" "$category"
|
|
json_add_boolean "featured" "$([ "$featured" = "true" ] && echo 1 || echo 0)"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
create_app() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var title title
|
|
json_get_var icon icon
|
|
json_get_var description description
|
|
json_get_var url url
|
|
json_get_var category category
|
|
json_get_var content content
|
|
|
|
json_init
|
|
|
|
if [ -z "$title" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Title required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local slug=$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-')
|
|
local date=$(date "+%Y-%m-%d %H:%M:%S")
|
|
|
|
local site_path=$(get_site_path)
|
|
local apps_dir="$site_path/source/apps"
|
|
local app_file="$apps_dir/${slug}.md"
|
|
|
|
[ -d "$apps_dir" ] || mkdir -p "$apps_dir"
|
|
|
|
if [ -f "$app_file" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "App with this slug already exists"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cat > "$app_file" << EOF
|
|
---
|
|
title: $title
|
|
date: $date
|
|
layout: app
|
|
icon: $icon
|
|
description: $description
|
|
url: $url
|
|
category: ${category:-tools}
|
|
featured: false
|
|
status: active
|
|
---
|
|
|
|
$content
|
|
EOF
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "slug" "$slug"
|
|
json_add_string "message" "App created"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Build & Deploy Methods
|
|
# ============================================
|
|
|
|
do_generate() {
|
|
json_init
|
|
|
|
if ! is_running; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Container not running"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output=$("$HEXOCTL" exec sh -c "cd /opt/hexojs/site && hexo generate" 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Site generated successfully"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
do_clean() {
|
|
json_init
|
|
|
|
if ! is_running; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Container not running"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output=$("$HEXOCTL" exec sh -c "cd /opt/hexojs/site && hexo clean" 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Site cleaned"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
do_deploy() {
|
|
json_init
|
|
|
|
if ! is_running; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Container not running"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local repo=$(uci_get deploy.repo)
|
|
if [ -z "$repo" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Deploy repository not configured"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Run deploy in background
|
|
"$HEXOCTL" exec sh -c "cd /opt/hexojs/site && hexo deploy" > /tmp/hexo-deploy.log 2>&1 &
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Deploy started"
|
|
json_add_string "log_file" "/tmp/hexo-deploy.log"
|
|
|
|
json_dump
|
|
}
|
|
|
|
get_deploy_status() {
|
|
json_init
|
|
|
|
if pgrep -f "hexo deploy" >/dev/null 2>&1; then
|
|
json_add_string "status" "running"
|
|
else
|
|
json_add_string "status" "idle"
|
|
fi
|
|
|
|
if [ -f /tmp/hexo-deploy.log ]; then
|
|
local log=$(tail -20 /tmp/hexo-deploy.log 2>/dev/null)
|
|
json_add_string "log" "$log"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Preview Methods
|
|
# ============================================
|
|
|
|
preview_start() {
|
|
json_init
|
|
|
|
if ! is_running; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Container not running. Start service first."
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local http_port=$(uci_get main.http_port) || http_port="4000"
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Preview server running"
|
|
json_add_int "port" "$http_port"
|
|
json_add_string "url" "http://$(uci -q get network.lan.ipaddr || echo "localhost"):$http_port"
|
|
|
|
json_dump
|
|
}
|
|
|
|
preview_status() {
|
|
json_init
|
|
|
|
local running=$(is_running && echo 1 || echo 0)
|
|
local http_port=$(uci_get main.http_port) || http_port="4000"
|
|
|
|
json_add_boolean "running" "$running"
|
|
json_add_int "port" "$http_port"
|
|
|
|
if [ "$running" = "1" ]; then
|
|
json_add_string "url" "http://$(uci -q get network.lan.ipaddr || echo "localhost"):$http_port"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Configuration Methods
|
|
# ============================================
|
|
|
|
get_config() {
|
|
json_init
|
|
|
|
local enabled=$(uci_get main.enabled) || enabled="0"
|
|
local http_port=$(uci_get main.http_port) || http_port="4000"
|
|
local data_path=$(uci_get main.data_path) || data_path="$DATA_PATH"
|
|
local active_site=$(uci_get main.active_site) || active_site="default"
|
|
local memory_limit=$(uci_get main.memory_limit) || memory_limit="512M"
|
|
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_add_int "http_port" "$http_port"
|
|
json_add_string "data_path" "$data_path"
|
|
json_add_string "active_site" "$active_site"
|
|
json_add_string "memory_limit" "$memory_limit"
|
|
|
|
# Site config
|
|
json_add_object "site"
|
|
json_add_string "title" "$(uci_get ${active_site}.title)"
|
|
json_add_string "subtitle" "$(uci_get ${active_site}.subtitle)"
|
|
json_add_string "author" "$(uci_get ${active_site}.author)"
|
|
json_add_string "language" "$(uci_get ${active_site}.language)"
|
|
json_add_string "theme" "$(uci_get ${active_site}.theme)"
|
|
json_add_string "url" "$(uci_get ${active_site}.url)"
|
|
json_close_object
|
|
|
|
# Deploy config
|
|
json_add_object "deploy"
|
|
json_add_string "type" "$(uci_get deploy.type)"
|
|
json_add_string "repo" "$(uci_get deploy.repo)"
|
|
json_add_string "branch" "$(uci_get deploy.branch)"
|
|
json_close_object
|
|
|
|
json_dump
|
|
}
|
|
|
|
save_config() {
|
|
read input
|
|
json_load "$input"
|
|
|
|
json_get_var enabled enabled
|
|
json_get_var http_port http_port
|
|
json_get_var title title
|
|
json_get_var subtitle subtitle
|
|
json_get_var author author
|
|
json_get_var language language
|
|
json_get_var url url
|
|
json_get_var deploy_repo deploy_repo
|
|
json_get_var deploy_branch deploy_branch
|
|
|
|
json_init
|
|
|
|
local active_site=$(uci_get main.active_site) || active_site="default"
|
|
|
|
[ -n "$enabled" ] && uci_set main.enabled "$enabled"
|
|
[ -n "$http_port" ] && uci_set main.http_port "$http_port"
|
|
[ -n "$title" ] && uci_set ${active_site}.title "$title"
|
|
[ -n "$subtitle" ] && uci_set ${active_site}.subtitle "$subtitle"
|
|
[ -n "$author" ] && uci_set ${active_site}.author "$author"
|
|
[ -n "$language" ] && uci_set ${active_site}.language "$language"
|
|
[ -n "$url" ] && uci_set ${active_site}.url "$url"
|
|
[ -n "$deploy_repo" ] && uci_set deploy.repo "$deploy_repo"
|
|
[ -n "$deploy_branch" ] && uci_set deploy.branch "$deploy_branch"
|
|
|
|
# Update site _config.yml
|
|
"$HEXOCTL" exec sh -c "cd /opt/hexojs && hexoctl update_site_config" 2>/dev/null || true
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Configuration saved"
|
|
|
|
json_dump
|
|
}
|
|
|
|
get_theme_config() {
|
|
json_init
|
|
|
|
json_add_string "default_mode" "$(uci_get theme_config.default_mode)"
|
|
json_add_boolean "allow_toggle" "$(uci_get theme_config.allow_toggle)"
|
|
json_add_string "accent_color" "$(uci_get theme_config.accent_color)"
|
|
json_add_string "logo_symbol" "$(uci_get theme_config.logo_symbol)"
|
|
json_add_string "logo_text" "$(uci_get theme_config.logo_text)"
|
|
|
|
json_dump
|
|
}
|
|
|
|
save_theme_config() {
|
|
read input
|
|
json_load "$input"
|
|
|
|
json_get_var default_mode default_mode
|
|
json_get_var allow_toggle allow_toggle
|
|
json_get_var accent_color accent_color
|
|
json_get_var logo_symbol logo_symbol
|
|
json_get_var logo_text logo_text
|
|
|
|
json_init
|
|
|
|
[ -n "$default_mode" ] && uci_set theme_config.default_mode "$default_mode"
|
|
[ -n "$allow_toggle" ] && uci_set theme_config.allow_toggle "$allow_toggle"
|
|
[ -n "$accent_color" ] && uci_set theme_config.accent_color "$accent_color"
|
|
[ -n "$logo_symbol" ] && uci_set theme_config.logo_symbol "$logo_symbol"
|
|
[ -n "$logo_text" ] && uci_set theme_config.logo_text "$logo_text"
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Theme configuration saved"
|
|
|
|
json_dump
|
|
}
|
|
|
|
list_presets() {
|
|
json_init
|
|
json_add_array "presets"
|
|
|
|
local presets_dir="/usr/share/hexojs/presets"
|
|
if [ -d "$presets_dir" ]; then
|
|
for f in "$presets_dir"/*.yml; do
|
|
[ -f "$f" ] || continue
|
|
|
|
local filename=$(basename "$f")
|
|
local id="${filename%.yml}"
|
|
local name=$(grep -m1 "^name:" "$f" | sed 's/^name:[[:space:]]*//')
|
|
local description=$(grep -m1 "^description:" "$f" | sed 's/^description:[[:space:]]*//')
|
|
local icon=$(grep -m1 "^icon:" "$f" | sed 's/^icon:[[:space:]]*//')
|
|
|
|
json_add_object
|
|
json_add_string "id" "$id"
|
|
json_add_string "name" "$name"
|
|
json_add_string "description" "$description"
|
|
json_add_string "icon" "$icon"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
apply_preset() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var preset_id preset_id
|
|
|
|
json_init
|
|
|
|
if [ -z "$preset_id" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Preset ID required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local preset_file="/usr/share/hexojs/presets/${preset_id}.yml"
|
|
if [ ! -f "$preset_file" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Preset not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local site_path=$(get_site_path)
|
|
local theme_config="$site_path/themes/cybermind/_config.yml"
|
|
|
|
if [ -f "$theme_config" ]; then
|
|
cp "$preset_file" "$theme_config"
|
|
fi
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Preset applied"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# GitHub Sync Methods
|
|
# ============================================
|
|
|
|
git_status() {
|
|
local site_path=$(get_site_path)
|
|
|
|
json_init
|
|
|
|
if [ ! -d "$site_path" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Site not found"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path" 2>/dev/null || {
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Cannot access site directory"
|
|
json_dump
|
|
return
|
|
}
|
|
|
|
# Check if it's a git repo
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_boolean "is_repo" 0
|
|
json_add_string "message" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_boolean "is_repo" 1
|
|
|
|
# Get remote URL
|
|
local remote=$(git remote get-url origin 2>/dev/null || echo "")
|
|
json_add_string "remote" "$remote"
|
|
|
|
# Get current branch
|
|
local branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
json_add_string "branch" "$branch"
|
|
|
|
# Get status summary
|
|
local modified=$(git status --porcelain 2>/dev/null | grep -c '^.M' || echo 0)
|
|
local untracked=$(git status --porcelain 2>/dev/null | grep -c '^??' || echo 0)
|
|
local staged=$(git status --porcelain 2>/dev/null | grep -c '^M' || echo 0)
|
|
|
|
json_add_int "modified" "$modified"
|
|
json_add_int "untracked" "$untracked"
|
|
json_add_int "staged" "$staged"
|
|
|
|
# Check for unpushed commits
|
|
local ahead=$(git rev-list --count @{u}..HEAD 2>/dev/null || echo 0)
|
|
local behind=$(git rev-list --count HEAD..@{u} 2>/dev/null || echo 0)
|
|
|
|
json_add_int "ahead" "$ahead"
|
|
json_add_int "behind" "$behind"
|
|
|
|
# Last commit info
|
|
local last_commit=$(git log -1 --format="%h - %s (%cr)" 2>/dev/null || echo "No commits")
|
|
json_add_string "last_commit" "$last_commit"
|
|
|
|
# Full status output
|
|
local status_output=$(git status --short 2>/dev/null | head -20)
|
|
json_add_string "status_output" "$status_output"
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_init() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var repo repo
|
|
json_get_var branch branch
|
|
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ -z "$repo" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Repository URL required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
[ -z "$branch" ] && branch="main"
|
|
|
|
# Initialize git if not already a repo
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
cd "$site_path" && git init >/dev/null 2>&1
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
# Remove existing origin if present
|
|
git remote remove origin 2>/dev/null || true
|
|
|
|
# Add remote
|
|
git remote add origin "$repo"
|
|
|
|
# Set default branch
|
|
git branch -M "$branch" 2>/dev/null || true
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Repository initialized with remote: $repo"
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_clone() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var repo repo
|
|
json_get_var branch branch
|
|
|
|
json_init
|
|
|
|
if [ -z "$repo" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Repository URL required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
[ -z "$branch" ] && branch="main"
|
|
|
|
local data_path=$(uci_get main.data_path) || data_path="$DATA_PATH"
|
|
local site_path="$data_path/site"
|
|
|
|
# Backup existing site if present
|
|
if [ -d "$site_path" ]; then
|
|
local backup="$data_path/site.backup.$(date +%Y%m%d%H%M%S)"
|
|
mv "$site_path" "$backup"
|
|
fi
|
|
|
|
# Clone repository
|
|
local output=$(git clone --branch "$branch" --single-branch "$repo" "$site_path" 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Repository cloned successfully"
|
|
json_add_string "branch" "$branch"
|
|
else
|
|
# Restore backup on failure
|
|
[ -d "$backup" ] && mv "$backup" "$site_path"
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Clone failed: $output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_pull() {
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
# Stash local changes first
|
|
git stash push -m "auto-stash-$(date +%Y%m%d%H%M%S)" 2>/dev/null
|
|
|
|
# Pull changes
|
|
local output=$(git pull --rebase 2>&1)
|
|
local result=$?
|
|
|
|
# Pop stash if it was created
|
|
git stash pop 2>/dev/null || true
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Pull successful"
|
|
json_add_string "output" "$output"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Pull failed: $output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_push() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var message message
|
|
json_get_var force force
|
|
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
# Check if there's a remote
|
|
local remote=$(git remote get-url origin 2>/dev/null || echo "")
|
|
if [ -z "$remote" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "No remote repository configured"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Stage all changes
|
|
git add -A
|
|
|
|
# Commit if there are staged changes
|
|
local staged=$(git diff --cached --quiet; echo $?)
|
|
if [ "$staged" = "1" ]; then
|
|
local commit_msg="${message:-Auto-commit from SecuBox CMS $(date +%Y-%m-%d\ %H:%M:%S)}"
|
|
git commit -m "$commit_msg" 2>/dev/null
|
|
fi
|
|
|
|
# Push changes
|
|
local push_args=""
|
|
[ "$force" = "1" ] && push_args="--force"
|
|
|
|
local output=$(git push $push_args origin HEAD 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Push successful"
|
|
json_add_string "output" "$output"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Push failed: $output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_fetch() {
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
local output=$(git fetch --all 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Fetch successful"
|
|
json_add_string "output" "$output"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Fetch failed: $output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_log() {
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_array "commits"
|
|
|
|
git log --oneline -20 2>/dev/null | while read line; do
|
|
local hash=$(echo "$line" | cut -d' ' -f1)
|
|
local msg=$(echo "$line" | cut -d' ' -f2-)
|
|
|
|
json_add_object
|
|
json_add_string "hash" "$hash"
|
|
json_add_string "message" "$msg"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
git_reset() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var hard hard
|
|
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
if [ "$hard" = "1" ]; then
|
|
local output=$(git reset --hard HEAD 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
git clean -fd 2>/dev/null
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Hard reset complete. All local changes discarded."
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Reset failed: $output"
|
|
fi
|
|
else
|
|
git reset HEAD 2>/dev/null
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Unstaged all changes"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_set_credentials() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var name name
|
|
json_get_var email email
|
|
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
[ -n "$name" ] && git config user.name "$name"
|
|
[ -n "$email" ] && git config user.email "$email"
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Git credentials configured"
|
|
|
|
json_dump
|
|
}
|
|
|
|
git_get_credentials() {
|
|
json_init
|
|
|
|
local site_path=$(get_site_path)
|
|
|
|
if [ ! -d "$site_path/.git" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Not a git repository"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
cd "$site_path"
|
|
|
|
local name=$(git config user.name 2>/dev/null || echo "")
|
|
local email=$(git config user.email 2>/dev/null || echo "")
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "name" "$name"
|
|
json_add_string "email" "$email"
|
|
|
|
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
|
|
}
|
|
|
|
# Publish to /www (portal)
|
|
publish_to_www() {
|
|
read input
|
|
json_load "$input"
|
|
json_get_var path path
|
|
|
|
json_init
|
|
|
|
if ! is_running; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Container not running"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Default path is /www/blog
|
|
[ -z "$path" ] && path="/www/blog"
|
|
|
|
# Set portal path in UCI for hexoctl
|
|
uci set hexojs.portal=hexojs
|
|
uci set hexojs.portal.path="$path"
|
|
uci commit hexojs
|
|
|
|
# Run publish command
|
|
local output=$("$HEXOCTL" publish 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Published to $path"
|
|
json_add_string "path" "$path"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Service Control
|
|
# ============================================
|
|
|
|
service_start() {
|
|
json_init
|
|
|
|
/etc/init.d/hexojs start >/dev/null 2>&1 &
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Service starting"
|
|
|
|
json_dump
|
|
}
|
|
|
|
service_stop() {
|
|
json_init
|
|
|
|
/etc/init.d/hexojs stop >/dev/null 2>&1
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Service stopped"
|
|
|
|
json_dump
|
|
}
|
|
|
|
service_restart() {
|
|
json_init
|
|
|
|
/etc/init.d/hexojs restart >/dev/null 2>&1 &
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Service restarting"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# ============================================
|
|
# Main dispatcher
|
|
# ============================================
|
|
|
|
case "$1" in
|
|
list)
|
|
cat << 'EOF'
|
|
{
|
|
"status": {},
|
|
"site_stats": {},
|
|
"list_posts": {},
|
|
"get_post": {"slug": "str"},
|
|
"create_post": {"title": "str", "content": "str", "categories": "str", "tags": "str", "excerpt": "str"},
|
|
"update_post": {"slug": "str", "title": "str", "content": "str", "categories": "str", "tags": "str", "excerpt": "str", "cover": "str"},
|
|
"delete_post": {"slug": "str"},
|
|
"publish_post": {"slug": "str"},
|
|
"list_drafts": {},
|
|
"search_posts": {"query": "str", "category": "str", "tag": "str"},
|
|
"list_categories": {},
|
|
"list_tags": {},
|
|
"list_media": {},
|
|
"delete_media": {"path": "str"},
|
|
"list_apps": {},
|
|
"create_app": {"title": "str", "icon": "str", "description": "str", "url": "str", "category": "str", "content": "str"},
|
|
"generate": {},
|
|
"clean": {},
|
|
"deploy": {},
|
|
"deploy_status": {},
|
|
"preview_start": {},
|
|
"preview_status": {},
|
|
"get_config": {},
|
|
"save_config": {"enabled": "bool", "http_port": "int", "title": "str", "subtitle": "str", "author": "str", "language": "str", "url": "str", "deploy_repo": "str", "deploy_branch": "str"},
|
|
"get_theme_config": {},
|
|
"save_theme_config": {"default_mode": "str", "allow_toggle": "bool", "accent_color": "str", "logo_symbol": "str", "logo_text": "str"},
|
|
"list_presets": {},
|
|
"apply_preset": {"preset_id": "str"},
|
|
"service_start": {},
|
|
"service_stop": {},
|
|
"service_restart": {},
|
|
"git_status": {},
|
|
"git_init": {"repo": "str", "branch": "str"},
|
|
"git_clone": {"repo": "str", "branch": "str"},
|
|
"git_pull": {},
|
|
"git_push": {"message": "str", "force": "bool"},
|
|
"git_fetch": {},
|
|
"git_log": {},
|
|
"git_reset": {"hard": "bool"},
|
|
"git_set_credentials": {"name": "str", "email": "str"},
|
|
"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"},
|
|
"publish_to_www": {"path": "str"}
|
|
}
|
|
EOF
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status) get_status ;;
|
|
site_stats) get_site_stats ;;
|
|
list_posts) list_posts ;;
|
|
get_post) get_post ;;
|
|
create_post) create_post ;;
|
|
update_post) update_post ;;
|
|
delete_post) delete_post ;;
|
|
publish_post) publish_post ;;
|
|
list_drafts) list_drafts ;;
|
|
search_posts) search_posts ;;
|
|
list_categories) list_categories ;;
|
|
list_tags) list_tags ;;
|
|
list_media) list_media ;;
|
|
delete_media) delete_media ;;
|
|
list_apps) list_apps ;;
|
|
create_app) create_app ;;
|
|
generate) do_generate ;;
|
|
clean) do_clean ;;
|
|
deploy) do_deploy ;;
|
|
deploy_status) get_deploy_status ;;
|
|
preview_start) preview_start ;;
|
|
preview_status) preview_status ;;
|
|
get_config) get_config ;;
|
|
save_config) save_config ;;
|
|
get_theme_config) get_theme_config ;;
|
|
save_theme_config) save_theme_config ;;
|
|
list_presets) list_presets ;;
|
|
apply_preset) apply_preset ;;
|
|
service_start) service_start ;;
|
|
service_stop) service_stop ;;
|
|
service_restart) service_restart ;;
|
|
git_status) git_status ;;
|
|
git_init) git_init ;;
|
|
git_clone) git_clone ;;
|
|
git_pull) git_pull ;;
|
|
git_push) git_push ;;
|
|
git_fetch) git_fetch ;;
|
|
git_log) git_log ;;
|
|
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 ;;
|
|
publish_to_www) publish_to_www ;;
|
|
*) echo '{"error": "Unknown method"}' ;;
|
|
esac
|
|
;;
|
|
esac
|