完成aptss对接

This commit is contained in:
2026-02-19 18:35:41 +08:00
parent 44a55249db
commit 69d9c23cff
7 changed files with 297 additions and 133 deletions

View File

@@ -180,7 +180,7 @@ ipcMain.on("queue-install", async (event, download_json) => {
} }
if (metalinkUrl && filename) { if (metalinkUrl && filename) {
execParams.push("aptss", "ssaudit", `${downloadDir}/${filename}`); execParams.push("ssinstall", `${downloadDir}/${filename}` , "--delete-after-install");
} else { } else {
execParams.push("aptss", "install", "-y", pkgname); execParams.push("aptss", "install", "-y", pkgname);
} }
@@ -395,36 +395,34 @@ ipcMain.handle("check-installed", async (_event, pkgname: string) => {
logger.warn("check-installed missing pkgname"); logger.warn("check-installed missing pkgname");
return false; return false;
} }
let isInstalled = false;
logger.info(`检查应用是否已安装: ${pkgname}`); logger.info(`检查应用是否已安装: ${pkgname}`);
const child = spawn( const checkScript = "/opt/spark-store/extras/check-is-installed";
SHELL_CALLER_PATH, let isInstalled = false;
["aptss", "list", "--installed", pkgname],
{
shell: true,
env: process.env,
},
);
let output = ""; const child = spawn(checkScript, [pkgname], {
shell: false,
child.stdout.on("data", (data) => { env: process.env,
output += data.toString();
}); });
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
child.on("error", (err) => {
logger.error(`check-installed 执行失败: ${err?.message || err}`);
resolve();
});
child.on("close", (code) => { child.on("close", (code) => {
if (code === 0 && output.includes(pkgname)) { if (code === 0) {
isInstalled = true; isInstalled = true;
logger.info(`应用已安装: ${pkgname}`); logger.info(`应用已安装: ${pkgname}`);
} else { } else {
logger.info(`应用未安装: ${pkgname}`); logger.info(`应用未安装: ${pkgname} (exit ${code})`);
} }
resolve(); resolve();
}); });
}); });
return isInstalled; return isInstalled;
}); });
@@ -447,7 +445,7 @@ ipcMain.on("remove-installed", async (_event, pkgname: string) => {
} }
const child = spawn( const child = spawn(
execCommand, execCommand,
[...execParams, "aptss", "remove", "-y", pkgname], [...execParams, "aptss", "remove", pkgname ],
{ {
shell: true, shell: true,
env: process.env, env: process.env,
@@ -571,7 +569,7 @@ ipcMain.handle("launch-app", async (_event, pkgname: string) => {
} }
const execCommand = "/opt/spark-store/extras/host-spawn"; const execCommand = "/opt/spark-store/extras/host-spawn";
const execParams = ["/opt/spark-store/extras/apm-launcher", "launch", pkgname]; const execParams = ["/opt/spark-store/extras/app-launcher", "launch", pkgname];
logger.info( logger.info(
`Launching app: ${pkgname} with command: ${execCommand} ${execParams.join(" ")}`, `Launching app: ${pkgname} with command: ${execCommand} ${execParams.join(" ")}`,

View File

@@ -1,102 +0,0 @@
#!/bin/bash
# ===== 日志函数(简化版)=====
log.info() { echo "INFO: $*"; }
log.warn() { echo "WARN: $*"; }
log.error() { echo "ERROR: $*"; }
log.debug() { :; } # APM 场景下可禁用 debug 日志
# ===== APM 专用桌面文件扫描(单文件)=====
function scan_apm_desktop_log() {
unset desktop_file_path
local pkg_name="$1"
local desktop_dir="/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkg_name}/entries/applications"
[ -d "$desktop_dir" ] || return 1
while IFS= read -r -d '' path; do
[ -f "$path" ] || continue
if ! grep -q 'NoDisplay=true' "$path" 2>/dev/null; then
log.info "Found valid APM desktop file: $path"
export desktop_file_path="$path"
return 0
fi
done < <(find "$desktop_dir" -name "*.desktop" -type f -print0 2>/dev/null)
return 1
}
# ===== APM 专用桌面文件扫描(多文件列表)=====
function scan_apm_desktop_list() {
local pkg_name="$1"
local desktop_dir="/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkg_name}/entries/applications"
local result=""
[ -d "$desktop_dir" ] || { echo ""; return; }
while IFS= read -r -d '' path; do
[ -f "$path" ] || continue
if ! grep -q 'NoDisplay=true' "$path" 2>/dev/null; then
result+="${path},"
fi
done < <(find "$desktop_dir" -name "*.desktop" -type f -print0 2>/dev/null)
echo "${result%,}"
}
# ===== 启动应用 =====
function launch_app() {
local desktop_path="${1#file://}"
local exec_cmd
# 提取并清理 Exec 行(移除字段代码如 %f %u 等)
exec_cmd=$(grep -m1 '^Exec=' "$desktop_path" | cut -d= -f2- | sed 's/%[fFuUdDnNickvm]*//g; s/^[[:space:]]*//; s/[[:space:]]*$//')
[ -z "$exec_cmd" ] && return 1
log.info "Launching: $exec_cmd"
${SHELL:-bash} -c "$exec_cmd" &
}
# 导出函数供 ACE 环境调用
export -f launch_app scan_apm_desktop_log scan_apm_desktop_list log.info log.error
# ===== 主逻辑 =====
[ $# -lt 2 ] && {
log.error "Usage: $0 {check|list|launch|start} <apm-package-name>"
exit 1
}
action="$1"
pkg_name="$2"
case "$action" in
check)
if scan_apm_desktop_log "$pkg_name"; then
exit 0
else
exit 1
fi
;;
list)
if result=$(scan_apm_desktop_list "$pkg_name"); [ -n "$result" ]; then
echo "$result"
exit 0
else
exit 1
fi
;;
launch|start)
if scan_apm_desktop_log "$pkg_name" && launch_app "$desktop_file_path"; then
exit 0
else
exit 1
fi
;;
*)
log.error "Invalid command: $action (supported: check|list|launch|start)"
exit 2
;;
esac

164
extras/app-launcher Executable file
View File

@@ -0,0 +1,164 @@
#!/bin/bash
# ===== ACE环境配置 =====
readonly ACE_ENVIRONMENTS=(
"bookworm-run:amber-ce-bookworm"
"trixie-run:amber-ce-trixie"
"deepin23-run:amber-ce-deepin23"
"sid-run:amber-ce-sid"
)
# ===== 日志和函数 =====
[ -f /opt/durapps/spark-store/bin/bashimport/log.amber ] && \
source /opt/durapps/spark-store/bin/bashimport/log.amber || {
log.info() { echo "INFO: $*"; }
log.warn() { echo "WARN: $*"; }
log.error() { echo "ERROR: $*"; }
log.debug() { echo "DEBUG: $*"; }
}
# ===== 功能函数 =====
function scan_desktop_file_log() {
unset desktop_file_path
local package_name=$1
# 标准desktop文件检测
while IFS= read -r path; do
[ -z "$(grep 'NoDisplay=true' "$path")" ] && {
log.info "Found valid desktop file: $path"
export desktop_file_path="$path"
return 0
}
done < <(dpkg -L "$package_name" 2>/dev/null | grep -E '/usr/share/applications/.*\.desktop$|/opt/apps/.*/entries/applications/.*\.desktop$')
# 深度环境特殊处理
while IFS= read -r path; do
[ -z "$(grep 'NoDisplay=true' "$path")" ] && {
log.info "Found deepin desktop file: $path"
export desktop_file_path="$path"
return 0
}
done < <(find /opt/apps/$package_name -path '*/entries/applications/*.desktop' 2>/dev/null)
return 1
}
function scan_desktop_file() {
local package_name=$1 result=""
# 标准结果收集
while IFS= read -r path; do
[ -z "$(grep 'NoDisplay=true' "$path")" ] && result+="$path,"
done < <(dpkg -L "$package_name" 2>/dev/null | grep -E '/usr/share/applications/.*\.desktop$|/opt/apps/.*/entries/applications/.*\.desktop$')
# 深度环境补充扫描
while IFS= read -r path; do
[ -z "$(grep 'NoDisplay=true' "$path")" ] && result+="$path,"
done < <(find /opt/apps/$package_name -path '*/entries/applications/*.desktop' 2>/dev/null)
echo "${result%,}"
}
function launch_app() {
local DESKTOP_FILE_PATH="${1#file://}"
# 提取并净化Exec命令
exec_command=$(grep -m1 '^Exec=' "$DESKTOP_FILE_PATH" | cut -d= -f2- | sed 's/%.//g')
[ -z "$exec_command" ] && return 1
[ ! -z "$IS_ACE_ENV" ] && HOST_PREFIX="host-spawn"
exec_command="${HOST_PREFIX} $exec_command"
log.info "Launching: $exec_command"
${SHELL:-bash} -c " $exec_command" &
}
# 导出函数以便在ACE环境中使用
export -f launch_app scan_desktop_file scan_desktop_file_log log.info log.warn log.debug log.error
# ===== ACE环境执行器 =====
function ace_runner() {
local action=$1
local target=$2
for ace_entry in "${ACE_ENVIRONMENTS[@]}"; do
local ace_cmd=${ace_entry%%:*}
local ace_env=${ace_entry#*:}
if ! command -v "$ace_cmd" >/dev/null; then
log.debug "$ace_cmd not found, skipping..."
continue
fi
log.info "Attempting in $ace_env environment..."
case "$action" in
check)
if "$ace_cmd" scan_desktop_file_log "$target"; then
log.info "Found desktop file in $ace_env"
return 0
fi
;;
list)
local result
if result=$("$ace_cmd" scan_desktop_file "$target"); then
echo "$result"
return 0
fi
;;
launch|start)
"$ace_cmd" scan_desktop_file_log "$target"
if desktop_path=$("$ace_cmd" scan_desktop_file_log "$target"); then
log.info "Launching from $ace_env..."
"$ace_cmd" launch_app $("$ace_cmd" scan_desktop_file "$target")
return 0
fi
;;
esac
log.debug "Attempt in $ace_env failed"
done
return 1
}
# ===== 主逻辑 =====
[ $# -lt 2 ] && {
log.error "Usage: $0 {check|launch|list|start} package_name/desktop_file"
exit 1
}
case $1 in
check)
# 当前环境检查
if scan_desktop_file_log "$2"; then
exit 0
else
# 非ACE环境下执行ACE环境扫描
[ -z "$IS_ACE_ENV" ] && ace_runner check "$2"
exit $?
fi
;;
list)
# 当前环境列表
if result=$(scan_desktop_file "$2"); then
echo "$result"
exit 0
else
# 非ACE环境下执行ACE环境扫描
[ -z "$IS_ACE_ENV" ] && ace_runner list "$2"
exit $?
fi
;;
launch|start)
# 当前环境启动
if scan_desktop_file_log "$2" && launch_app "$desktop_file_path"; then
exit 0
else
# 非ACE环境下通过ACE环境启动
[ -z "$IS_ACE_ENV" ] && ace_runner launch "$2"
exit $?
fi
;;
*)
log.error "Invalid command: $1"
exit 2
;;
esac

45
extras/check-is-installed Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
readonly ACE_ENVIRONMENTS=(
"bookworm-run:amber-ce-bookworm"
"trixie-run:amber-ce-trixie"
"deepin23-run:amber-ce-deepin23"
"sid-run:amber-ce-sid"
)
dpkg -s "$1" > /dev/null
RET="$?"
if [[ "$RET" != "0" ]] &&[[ "$IS_ACE_ENV" == "" ]];then ## 如果未在ACE环境中
for ace_entry in "${ACE_ENVIRONMENTS[@]}"; do
ace_cmd=${ace_entry%%:*}
if command -v "$ace_cmd" >/dev/null 2>&1; then
echo "----------------------------------------"
echo "正在检查 $ace_cmd 环境的安装..."
echo "----------------------------------------"
# 在ACE环境中执行安装检测
$ace_cmd dpkg -l | grep "^ii $1 " > /dev/null
try_run_ret="$?"
# 最终检测结果处理
if [ "$try_run_ret" -eq 0 ]; then
echo "----------------------------------------"
echo "在 $ace_cmd 环境中找到了安装"
echo "----------------------------------------"
exit $try_run_ret
else
echo "----------------------------------------"
echo "在 $ace_cmd 环境中未能找到安装,继续查找"
echo "----------------------------------------"
fi
fi
done
echo "----------------------------------------"
echo "所有已安装的 ACE 环境中未能找到安装,退出"
echo "----------------------------------------"
exit "$RET"
fi
## 如果在ACE环境中或者未出错
exit "$RET"

View File

@@ -2,18 +2,77 @@
# 检查是否提供了至少一个参数 # 检查是否提供了至少一个参数
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
echo "错误:未提供命令参数。用法: $0 aptss <子命令> [参数...]" echo "错误:未提供命令参数。用法: $0 [aptss|ssinstall] <子命令> [参数...]"
exit 1 exit 1
fi fi
# 严格验证第一个参数必须是 "aptss" # 获取第一个参数
if [[ "$1" != "aptss" ]]; then first_arg="$1"
echo "拒绝执行:仅允许执行 'aptss' 命令。收到的第一个参数: '$1'"
# 根据第一个参数决定执行哪个命令
if [[ "$first_arg" == "ssinstall" ]]; then
# 执行 ssinstall 命令(跳过第一个参数)
/usr/bin/ssinstall "${@:2}" 2>&1
exit_code=$?
elif [[ "$first_arg" == "aptss" ]]; then
# 检查是否为 remove 子命令(第二个参数)
if [[ "$2" == "remove" ]]; then
# 获取要卸载的软件包名称(第三个参数及以后)
packages="${@:3}"
# 检查可用的对话框程序
if command -v garma &> /dev/null; then
# 使用 garma 询问确认
garma --question \
--title="确认卸载" \
--text="正在准备卸载: $packages\n若这是您下达的卸载指令请选择确认继续卸载" \
--ok-label="确认卸载" \
--cancel-label="取消" \
--width=400
if [[ $? -eq 0 ]]; then
# 用户确认,执行卸载
/usr/bin/aptss "${@:2}" -y 2>&1
exit_code=$?
else
# 用户取消
echo "操作已取消"
exit 0
fi
elif command -v zenity &> /dev/null; then
# 使用 zenity 询问确认
zenity --question \
--title="确认卸载" \
--text="正在准备卸载: $packages\n\n若这是您下达的卸载指令请选择确认继续卸载" \
--ok-label="确认卸载" \
--cancel-label="取消" \
--width=400
if [[ $? -eq 0 ]]; then
# 用户确认,执行卸载
/usr/bin/aptss "${@:2}" -y 2>&1
exit_code=$?
else
# 用户取消
echo "操作已取消"
exit 0
fi
else
# 既没有 garma 也没有 zenity拒绝卸载
echo "错误:未找到 garma 或 zenity无法显示确认对话框。卸载操作已拒绝。"
exit 1
fi
else
# 非 remove 命令,直接执行
/usr/bin/aptss "${@:2}" 2>&1
exit_code=$?
fi
else
# 其他情况,拒绝执行
echo "拒绝执行:仅允许执行 'aptss' 或 'ssinstall' 命令。收到的第一个参数: '$first_arg'"
exit 1 exit 1
fi fi
# 执行 aptss 命令(跳过第一个参数 "aptss"
/usr/bin/aptss "${@:2}" 2>&1
exit_code=$?
exit $exit_code exit $exit_code

View File

@@ -89,7 +89,7 @@
</div> </div>
</div> </div>
<div <!-- <div
class="mt-4 rounded-2xl border border-slate-200/60 bg-slate-50/70 px-4 py-3 text-sm text-slate-600 dark:border-slate-800/60 dark:bg-slate-900/60 dark:text-slate-300" class="mt-4 rounded-2xl border border-slate-200/60 bg-slate-50/70 px-4 py-3 text-sm text-slate-600 dark:border-slate-800/60 dark:bg-slate-900/60 dark:text-slate-300"
> >
首次安装 APM 后需要重启系统以在启动器中看到应用入口可前往 首次安装 APM 后需要重启系统以在启动器中看到应用入口可前往
@@ -100,7 +100,7 @@
>APM Releases</a >APM Releases</a
> >
获取 APM 获取 APM
</div> </div> -->
<div v-if="screenshots.length" class="mt-6 grid gap-3 sm:grid-cols-2"> <div v-if="screenshots.length" class="mt-6 grid gap-3 sm:grid-cols-2">
<img <img

View File

@@ -4,12 +4,12 @@
type="button" type="button"
class="inline-flex items-center gap-2 rounded-2xl bg-gradient-to-r from-brand to-brand-dark px-4 py-2 text-sm font-semibold text-white shadow-lg transition hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/40" class="inline-flex items-center gap-2 rounded-2xl bg-gradient-to-r from-brand to-brand-dark px-4 py-2 text-sm font-semibold text-white shadow-lg transition hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/40"
@click="handleUpdate" @click="handleUpdate"
title="启动 apm-update-tool" title="检查可更新的软件包列表"
> >
<i class="fas fa-sync-alt"></i> <i class="fas fa-sync-alt"></i>
<span>软件更新</span> <span>软件更新</span>
</button> </button>
<button <!-- <button
type="button" type="button"
class="inline-flex items-center gap-2 rounded-2xl bg-slate-900/90 px-4 py-2 text-sm font-semibold text-white shadow-lg shadow-slate-900/40 transition hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-900/40 dark:bg-white/90 dark:text-slate-900" class="inline-flex items-center gap-2 rounded-2xl bg-slate-900/90 px-4 py-2 text-sm font-semibold text-white shadow-lg shadow-slate-900/40 transition hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-900/40 dark:bg-white/90 dark:text-slate-900"
@click="handleList" @click="handleList"
@@ -17,7 +17,7 @@
> >
<i class="fas fa-download"></i> <i class="fas fa-download"></i>
<span>应用管理</span> <span>应用管理</span>
</button> </button> -->
</div> </div>
</template> </template>