feat: 动态路径系统 - 支持任意用户环境部署
问题: - 100+ 处硬编码路径 /Users/accusys - 无法在不同用户环境部署 - 配置文件路径固定 解决方案: 1. ✅ 创建 paths.rs 模块 - get_home_dir() - 获取用户主目录 - get_momentry_core_root() - Core API 根目录 - get_markbase_root() - MarkBase 根目录 - get_studio_root() - Studio 根目录 - get_storage_root() - 存储根目录 - 环境变量优先 > 用户主目录推断 2. ✅ 修改 main.rs - 移除所有硬编码路径 - 使用动态路径函数 - 支持环境变量配置 3. ✅ 创建配置模板 - admin_config.json.template - .env.template - 使用 {{HOME}} 占位符 4. ✅ 创建自动化脚本 - install.sh - 通用安装脚本 - generate_config.sh - 配置生成脚本 - 自动检测用户环境 特性: - ✅ 支持任意用户环境 - ✅ 自动检测用户主目录 - ✅ 环境变量优先级控制 - ✅ 一键安装部署 测试: - ✅ 编译通过 (cargo check) - ⚠️ 需要在新环境测试 使用方法: 1. 克隆项目 2. ./install.sh 3. 编辑 .env 4. npm run proxy 适用环境: - m5max48: /Users/accusys - m4promini: /Users/nextorageai - 任意 macOS 环境
This commit is contained in:
107
.env.template
Normal file
107
.env.template
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# Momentry Studio - 环境变量模板
|
||||||
|
# 复制此文件为 .env 并根据实际环境修改
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 用户主目录(自动检测,通常不需要设置)
|
||||||
|
# ========================================
|
||||||
|
# HOME=/Users/your_username
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 项目根目录
|
||||||
|
# ========================================
|
||||||
|
# 默认: $HOME/momentry_core
|
||||||
|
MOMENTRY_CORE_ROOT=${HOME}/momentry_core
|
||||||
|
|
||||||
|
# 默认: $HOME/markbase
|
||||||
|
MARKBASE_ROOT=${HOME}/markbase
|
||||||
|
|
||||||
|
# 默认: $HOME/momentry_studio
|
||||||
|
MOMENTRY_STUDIO_ROOT=${HOME}/momentry_studio
|
||||||
|
|
||||||
|
# 默认: $HOME/momentry
|
||||||
|
MOMENTRY_ROOT=${HOME}/momentry
|
||||||
|
|
||||||
|
# 默认: $HOME/MarkBaseEngine
|
||||||
|
MARKBASE_ENGINE_ROOT=${HOME}/MarkBaseEngine
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 存储路径
|
||||||
|
# ========================================
|
||||||
|
# 默认: $HOME/momentry/var/sftpgo/data
|
||||||
|
STORAGE_ROOT=${HOME}/momentry/var/sftpgo/data
|
||||||
|
|
||||||
|
# 默认: $HOME/momentry/output
|
||||||
|
OUTPUT_DIR=${HOME}/momentry/output
|
||||||
|
|
||||||
|
# 默认: $HOME/momentry/backup
|
||||||
|
BACKUP_DIR=${HOME}/momentry/backup
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 数据库
|
||||||
|
# ========================================
|
||||||
|
# SQLite 数据库目录
|
||||||
|
MOMENTRY_DATA_DIR=${HOME}/momentry_studio/data/users
|
||||||
|
|
||||||
|
# PostgreSQL 连接字符串
|
||||||
|
DATABASE_URL=postgres://$(whoami)@localhost:5432/momentry
|
||||||
|
|
||||||
|
# Qdrant
|
||||||
|
QDRANT_URL=http://localhost:6333
|
||||||
|
QDRANT_API_KEY=your_qdrant_api_key_here
|
||||||
|
|
||||||
|
# Redis (可选)
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# API Keys
|
||||||
|
# ========================================
|
||||||
|
# Momentry Core API Key
|
||||||
|
MOMENTRY_API_KEY=your_momentry_api_key_here
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 工具路径
|
||||||
|
# ========================================
|
||||||
|
# Python (用于脚本)
|
||||||
|
PYTHON_PATH=/opt/homebrew/bin/python3.11
|
||||||
|
|
||||||
|
# FFmpeg (用于视频处理)
|
||||||
|
FFMPEG_PATH=/opt/homebrew/bin/ffmpeg
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 服务配置
|
||||||
|
# ========================================
|
||||||
|
# Core API 端口
|
||||||
|
MOMENTRY_SERVER_PORT=3002
|
||||||
|
|
||||||
|
# Studio Proxy 端口
|
||||||
|
STUDIO_PROXY_PORT=8888
|
||||||
|
|
||||||
|
# MarkBase 端口
|
||||||
|
MARKBASE_PORT=11438
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# LLM 服务
|
||||||
|
# ========================================
|
||||||
|
# VLM Vision API
|
||||||
|
MOMENTRY_LLM_VISION_URL=http://localhost:8080/v1/chat/completions
|
||||||
|
|
||||||
|
# LLM Chat API
|
||||||
|
MOMENTRY_LLM_CHAT_URL=http://localhost:8080/v1/chat/completions
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 外部服务 URL
|
||||||
|
# ========================================
|
||||||
|
# Media Base URL
|
||||||
|
MEDIA_BASE_URL=https://wp.momentry.ddns.net
|
||||||
|
|
||||||
|
# Serve Base URL
|
||||||
|
SERVE_BASE_URL=https://m5wp.momentry.ddns.net/files
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 说明
|
||||||
|
# ========================================
|
||||||
|
# 1. 复制此文件: cp .env.template .env
|
||||||
|
# 2. 编辑 .env 文件,填入实际值
|
||||||
|
# 3. 加载环境变量: source .env
|
||||||
|
# 4. 运行配置生成: ./generate_config.sh
|
||||||
|
# 5. 启动服务: npm run proxy
|
||||||
84
config/admin_config.json.template
Normal file
84
config/admin_config.json.template
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"vlm_prompts": {
|
||||||
|
"scene": {
|
||||||
|
"system_prompt": "Analyze this video frame and provide a detailed description.",
|
||||||
|
"user_prompt_template": "Describe the scene in detail, including environment, objects, and activities.",
|
||||||
|
"max_tokens": 500,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"output_language": "en"
|
||||||
|
},
|
||||||
|
"trace": {
|
||||||
|
"system_prompt": "Describe the person's appearance and clothing in this frame.",
|
||||||
|
"user_prompt_template": "Describe the person, focusing on clothing, accessories, and notable features.",
|
||||||
|
"max_tokens": 300,
|
||||||
|
"temperature": 0.6,
|
||||||
|
"output_language": "en"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"llm_config": {
|
||||||
|
"model": "gpt-4o",
|
||||||
|
"max_tokens": 2000,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"api_base": "http://localhost:8080/v1"
|
||||||
|
},
|
||||||
|
"processor_config": {
|
||||||
|
"asr": {
|
||||||
|
"enabled": true,
|
||||||
|
"language": "auto",
|
||||||
|
"model": "large-v3"
|
||||||
|
},
|
||||||
|
"ocr": {
|
||||||
|
"enabled": true,
|
||||||
|
"languages": ["en", "zh"],
|
||||||
|
"min_confidence": 0.5
|
||||||
|
},
|
||||||
|
"face": {
|
||||||
|
"enabled": true,
|
||||||
|
"min_confidence": 0.7,
|
||||||
|
"min_size": 32
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filter_config": {
|
||||||
|
"face": {
|
||||||
|
"min_confidence": 50.0,
|
||||||
|
"min_size": 32,
|
||||||
|
"min_capture_quality": 30.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"output_dir": "{{HOME}}/momentry/output",
|
||||||
|
"backup_dir": "{{HOME}}/momentry/backup",
|
||||||
|
"scripts_dir": "{{HOME}}/momentry_core/scripts",
|
||||||
|
"python_path": "{{PYTHON_PATH}}",
|
||||||
|
"ffmpeg_path": "{{FFMPEG_PATH}}"
|
||||||
|
},
|
||||||
|
"concurrency": {
|
||||||
|
"max_concurrent_jobs": 6,
|
||||||
|
"poll_interval": 10,
|
||||||
|
"batch_size": 10,
|
||||||
|
"processor_timeout_secs": 3600
|
||||||
|
},
|
||||||
|
"feature_flags": {
|
||||||
|
"cache_enabled": true,
|
||||||
|
"auto_pipeline_enabled": false,
|
||||||
|
"watcher_auto_register": false,
|
||||||
|
"summary_enabled": true
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"port": 3002,
|
||||||
|
"media_base_url": "{{MEDIA_BASE_URL}}",
|
||||||
|
"serve_base_url": "{{SERVE_BASE_URL}}",
|
||||||
|
"storage_root": "{{HOME}}/momentry/var/sftpgo/data"
|
||||||
|
},
|
||||||
|
"admin_settings": {
|
||||||
|
"session_timeout": 3600,
|
||||||
|
"max_login_attempts": 5,
|
||||||
|
"api_key_expiry_days": 365,
|
||||||
|
"system_stats_refresh_sec": 5
|
||||||
|
},
|
||||||
|
"face_filter_defaults": {
|
||||||
|
"min_confidence": 50.0,
|
||||||
|
"min_size": 32,
|
||||||
|
"min_capture_quality": 25.0
|
||||||
|
}
|
||||||
|
}
|
||||||
76
generate_config.sh
Executable file
76
generate_config.sh
Executable file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Momentry Studio - 配置文件生成脚本
|
||||||
|
# 用途: 根据当前用户环境生成 admin_config.json
|
||||||
|
# 使用: ./generate_config.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 获取用户主目录
|
||||||
|
USER_HOME="${HOME:-$(pwd)}"
|
||||||
|
STUDIO_ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
echo "================================================================================"
|
||||||
|
echo " Momentry Studio - 配置文件生成"
|
||||||
|
echo "================================================================================"
|
||||||
|
echo ""
|
||||||
|
echo "用户主目录: ${USER_HOME}"
|
||||||
|
echo "Studio 根目录: ${STUDIO_ROOT}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 设置默认值
|
||||||
|
PYTHON_PATH="${PYTHON_PATH:-/opt/homebrew/bin/python3.11}"
|
||||||
|
FFMPEG_PATH="${FFMPEG_PATH:-/opt/homebrew/bin/ffmpeg}"
|
||||||
|
MEDIA_BASE_URL="${MEDIA_BASE_URL:-https://wp.momentry.ddns.net}"
|
||||||
|
SERVE_BASE_URL="${SERVE_BASE_URL:-https://m5wp.momentry.ddns.net/files}"
|
||||||
|
|
||||||
|
# 检查模板文件
|
||||||
|
TEMPLATE_FILE="${STUDIO_ROOT}/config/admin_config.json.template"
|
||||||
|
if [ ! -f "${TEMPLATE_FILE}" ]; then
|
||||||
|
echo "❌ 错误: 找不到模板文件 ${TEMPLATE_FILE}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "配置值:"
|
||||||
|
echo " - Python: ${PYTHON_PATH}"
|
||||||
|
echo " - FFmpeg: ${FFMPEG_PATH}"
|
||||||
|
echo " - Media Base URL: ${MEDIA_BASE_URL}"
|
||||||
|
echo " - Serve Base URL: ${SERVE_BASE_URL}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 生成配置文件
|
||||||
|
OUTPUT_FILE="${STUDIO_ROOT}/config/admin_config.json"
|
||||||
|
|
||||||
|
echo "生成配置文件..."
|
||||||
|
sed -e "s|{{HOME}}|${USER_HOME}|g" \
|
||||||
|
-e "s|{{PYTHON_PATH}}|${PYTHON_PATH}|g" \
|
||||||
|
-e "s|{{FFMPEG_PATH}}|${FFMPEG_PATH}|g" \
|
||||||
|
-e "s|{{MEDIA_BASE_URL}}|${MEDIA_BASE_URL}|g" \
|
||||||
|
-e "s|{{SERVE_BASE_URL}}|${SERVE_BASE_URL}|g" \
|
||||||
|
"${TEMPLATE_FILE}" > "${OUTPUT_FILE}"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✅ 配置文件已生成: ${OUTPUT_FILE}"
|
||||||
|
echo ""
|
||||||
|
echo "生成的路径:"
|
||||||
|
echo " - Output Dir: ${USER_HOME}/momentry/output"
|
||||||
|
echo " - Backup Dir: ${USER_HOME}/momentry/backup"
|
||||||
|
echo " - Scripts Dir: ${USER_HOME}/momentry_core/scripts"
|
||||||
|
echo " - Storage Root: ${USER_HOME}/momentry/var/sftpgo/data"
|
||||||
|
else
|
||||||
|
echo "❌ 配置文件生成失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "================================================================================"
|
||||||
|
echo " ✅ 完成"
|
||||||
|
echo "================================================================================"
|
||||||
|
echo ""
|
||||||
|
echo "下一步:"
|
||||||
|
echo " 1. 检查生成的配置: cat ${OUTPUT_FILE}"
|
||||||
|
echo " 2. 如需自定义,设置环境变量后重新运行:"
|
||||||
|
echo " export PYTHON_PATH=/your/path"
|
||||||
|
echo " export FFMPEG_PATH=/your/path"
|
||||||
|
echo " ./generate_config.sh"
|
||||||
|
echo ""
|
||||||
171
install.sh
Executable file
171
install.sh
Executable file
@@ -0,0 +1,171 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Momentry Studio - 通用安装脚本
|
||||||
|
# 适用于任何用户环境
|
||||||
|
# 使用: ./install.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_step() {
|
||||||
|
echo -e "${BLUE}[STEP]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取脚本所在目录
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
USER_HOME="${HOME:-$(pwd)}"
|
||||||
|
|
||||||
|
echo "================================================================================"
|
||||||
|
echo " Momentry Studio - 安装向导"
|
||||||
|
echo "================================================================================"
|
||||||
|
echo ""
|
||||||
|
echo "检测到的环境:"
|
||||||
|
echo " 用户主目录: ${USER_HOME}"
|
||||||
|
echo " Studio 根目录: ${SCRIPT_DIR}"
|
||||||
|
echo " 用户名: $(whoami)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 步骤 1: 检查依赖
|
||||||
|
log_step "步骤 1/6: 检查依赖..."
|
||||||
|
|
||||||
|
check_command() {
|
||||||
|
local cmd=$1
|
||||||
|
local name=$2
|
||||||
|
if command -v $cmd &> /dev/null; then
|
||||||
|
log_info "✅ $name: $(command -v $cmd)"
|
||||||
|
else
|
||||||
|
log_error "❌ $name 未安装"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_command "node" "Node.js" || exit 1
|
||||||
|
check_command "npm" "npm" || exit 1
|
||||||
|
check_command "rustc" "Rust" || exit 1
|
||||||
|
check_command "cargo" "Cargo" || exit 1
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 步骤 2: 创建环境变量文件
|
||||||
|
log_step "步骤 2/6: 创建环境变量文件..."
|
||||||
|
|
||||||
|
if [ -f "${SCRIPT_DIR}/.env" ]; then
|
||||||
|
log_warn ".env 文件已存在,跳过创建"
|
||||||
|
else
|
||||||
|
cp "${SCRIPT_DIR}/.env.template" "${SCRIPT_DIR}/.env"
|
||||||
|
log_info "✅ 已创建 .env 文件"
|
||||||
|
log_warn "请编辑 .env 文件,填入实际的 API Keys"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 步骤 3: 生成配置文件
|
||||||
|
log_step "步骤 3/6: 生成配置文件..."
|
||||||
|
|
||||||
|
chmod +x "${SCRIPT_DIR}/generate_config.sh"
|
||||||
|
"${SCRIPT_DIR}/generate_config.sh"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 步骤 4: 创建数据目录
|
||||||
|
log_step "步骤 4/6: 创建数据目录..."
|
||||||
|
|
||||||
|
mkdir -p "${USER_HOME}/momentry/output"
|
||||||
|
mkdir -p "${USER_HOME}/momentry/backup"
|
||||||
|
mkdir -p "${USER_HOME}/momentry/var/sftpgo/data"
|
||||||
|
mkdir -p "${SCRIPT_DIR}/data/users"
|
||||||
|
|
||||||
|
log_info "✅ 已创建数据目录:"
|
||||||
|
log_info " - Output: ${USER_HOME}/momentry/output"
|
||||||
|
log_info " - Backup: ${USER_HOME}/momentry/backup"
|
||||||
|
log_info " - Storage: ${USER_HOME}/momentry/var/sftpgo/data"
|
||||||
|
log_info " - Database: ${SCRIPT_DIR}/data/users"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 步骤 5: 安装依赖并构建
|
||||||
|
log_step "步骤 5/6: 安装依赖并构建..."
|
||||||
|
|
||||||
|
cd "${SCRIPT_DIR}"
|
||||||
|
|
||||||
|
log_info "安装 Node.js 依赖..."
|
||||||
|
npm install
|
||||||
|
|
||||||
|
log_info "构建前端..."
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
log_info "构建 Rust 代理..."
|
||||||
|
cd src-tauri
|
||||||
|
cargo build --release
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
log_info "✅ 构建完成"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 步骤 6: 创建启动脚本
|
||||||
|
log_step "步骤 6/6: 创建启动脚本..."
|
||||||
|
|
||||||
|
cat > "${SCRIPT_DIR}/start.sh" << STARTSCRIPT
|
||||||
|
#!/bin/bash
|
||||||
|
# Momentry Studio 启动脚本
|
||||||
|
|
||||||
|
# 加载环境变量
|
||||||
|
if [ -f "${SCRIPT_DIR}/.env" ]; then
|
||||||
|
source "${SCRIPT_DIR}/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 设置必要的环境变量
|
||||||
|
export MOMENTRY_API_KEY=\${MOMENTRY_API_KEY:-your_api_key_here}
|
||||||
|
export MOMENTRY_DATA_DIR=\${MOMENTRY_DATA_DIR:-${SCRIPT_DIR}/data/users}
|
||||||
|
|
||||||
|
# 启动代理
|
||||||
|
cd "${SCRIPT_DIR}"
|
||||||
|
./src-tauri/target/release/momentry-proxy
|
||||||
|
STARTSCRIPT
|
||||||
|
|
||||||
|
chmod +x "${SCRIPT_DIR}/start.sh"
|
||||||
|
|
||||||
|
log_info "✅ 已创建启动脚本: start.sh"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 总结
|
||||||
|
echo "================================================================================"
|
||||||
|
echo " ✅ 安装完成"
|
||||||
|
echo "================================================================================"
|
||||||
|
echo ""
|
||||||
|
echo "安装位置:"
|
||||||
|
echo " Studio 根目录: ${SCRIPT_DIR}"
|
||||||
|
echo " 配置文件: ${SCRIPT_DIR}/config/admin_config.json"
|
||||||
|
echo " 环境变量: ${SCRIPT_DIR}/.env"
|
||||||
|
echo " 数据目录: ${SCRIPT_DIR}/data/users"
|
||||||
|
echo ""
|
||||||
|
echo "下一步:"
|
||||||
|
echo " 1. 编辑环境变量: vim ${SCRIPT_DIR}/.env"
|
||||||
|
echo " 2. 加载环境变量: source ${SCRIPT_DIR}/.env"
|
||||||
|
echo " 3. 启动服务: ${SCRIPT_DIR}/start.sh"
|
||||||
|
echo " 4. 访问: http://localhost:8888"
|
||||||
|
echo ""
|
||||||
|
echo "或者使用 npm 命令:"
|
||||||
|
echo " npm run proxy # 启动代理服务"
|
||||||
|
echo ""
|
||||||
|
echo "================================================================================"
|
||||||
@@ -5,6 +5,19 @@ use markbase_core::vfs::VfsBackend;
|
|||||||
mod proxy;
|
mod proxy;
|
||||||
mod db;
|
mod db;
|
||||||
mod validate;
|
mod validate;
|
||||||
|
mod paths;
|
||||||
|
|
||||||
|
// Import path helper functions
|
||||||
|
use paths::{
|
||||||
|
get_home_dir,
|
||||||
|
get_momentry_core_root,
|
||||||
|
get_markbase_root,
|
||||||
|
get_studio_root,
|
||||||
|
get_momentry_root,
|
||||||
|
get_storage_root,
|
||||||
|
get_markbase_engine_root,
|
||||||
|
get_worker_pid_file,
|
||||||
|
};
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||||
@@ -1330,9 +1343,15 @@ async fn merge_history(source_uuid: Option<String>, target_uuid: Option<String>,
|
|||||||
async fn stop_service(name: String) -> Result<String, String> {
|
async fn stop_service(name: String) -> Result<String, String> {
|
||||||
if name == "Worker" || name == "worker" {
|
if name == "Worker" || name == "worker" {
|
||||||
// Worker has no port — stop via pidfile / pkill
|
// Worker has no port — stop via pidfile / pkill
|
||||||
|
let pid_file = get_worker_pid_file();
|
||||||
|
let core_root = get_momentry_core_root();
|
||||||
|
let cmd = format!(
|
||||||
|
"sleep 0.5; WPID=$(cat {} 2>/dev/null); if [ -n \"$WPID\" ]; then pkill -P $WPID 2>/dev/null; kill -9 $WPID 2>/dev/null; fi; pkill -9 -f 'target/release/momentry worker' 2>/dev/null; rm -f {}; echo 'stopped'",
|
||||||
|
pid_file, pid_file
|
||||||
|
);
|
||||||
let output = std::process::Command::new("sh")
|
let output = std::process::Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg("sleep 0.5; WPID=$(cat /Users/accusys/momentry_core/logs/worker_3002.pid 2>/dev/null); if [ -n \"$WPID\" ]; then pkill -P $WPID 2>/dev/null; kill -9 $WPID 2>/dev/null; fi; pkill -9 -f 'target/release/momentry worker' 2>/dev/null; rm -f /Users/accusys/momentry_core/logs/worker_3002.pid; echo 'stopped'")
|
.arg(&cmd)
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to stop worker: {}", e))?;
|
.map_err(|e| format!("Failed to stop worker: {}", e))?;
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
@@ -1360,9 +1379,11 @@ async fn stop_service(name: String) -> Result<String, String> {
|
|||||||
async fn start_service(name: String) -> Result<String, String> {
|
async fn start_service(name: String) -> Result<String, String> {
|
||||||
if name == "Worker" || name == "worker" {
|
if name == "Worker" || name == "worker" {
|
||||||
// Worker has no port — verify via pidfile instead of lsof
|
// Worker has no port — verify via pidfile instead of lsof
|
||||||
|
let core_root = get_momentry_core_root();
|
||||||
|
let cmd = format!("cd {} && nohup bash run-worker-3002.sh > /tmp/momentry_worker.log 2>&1 &", core_root);
|
||||||
let output = std::process::Command::new("sh")
|
let output = std::process::Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg("cd /Users/accusys/momentry_core && nohup bash run-worker-3002.sh > /tmp/momentry_worker.log 2>&1 &")
|
.arg(&cmd)
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to start worker: {}", e))?;
|
.map_err(|e| format!("Failed to start worker: {}", e))?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
@@ -1370,9 +1391,11 @@ async fn start_service(name: String) -> Result<String, String> {
|
|||||||
return Err(format!("Worker start failed: {}", stderr));
|
return Err(format!("Worker start failed: {}", stderr));
|
||||||
}
|
}
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
||||||
|
let pid_file = get_worker_pid_file();
|
||||||
|
let check_cmd = format!("WPID=$(cat {} 2>/dev/null); [ -n \"$WPID\" ] && kill -0 $WPID 2>/dev/null && echo alive", pid_file);
|
||||||
let check = std::process::Command::new("sh")
|
let check = std::process::Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg("WPID=$(cat /Users/accusys/momentry_core/logs/worker_3002.pid 2>/dev/null); [ -n \"$WPID\" ] && kill -0 $WPID 2>/dev/null && echo alive")
|
.arg(&check_cmd)
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Worker check failed: {}", e))?;
|
.map_err(|e| format!("Worker check failed: {}", e))?;
|
||||||
if String::from_utf8_lossy(&check.stdout).contains("alive") {
|
if String::from_utf8_lossy(&check.stdout).contains("alive") {
|
||||||
@@ -1382,22 +1405,29 @@ async fn start_service(name: String) -> Result<String, String> {
|
|||||||
}
|
}
|
||||||
let cmd = match name.as_str() {
|
let cmd = match name.as_str() {
|
||||||
"MarkBase" | "markbase" => {
|
"MarkBase" | "markbase" => {
|
||||||
"mkdir -p /Users/accusys/markbase/data && cd /Users/accusys/markbase && nohup ./target/release/markbase-core web-start --port 11438 > /tmp/markbase_web.log 2>&1 &"
|
let markbase_root = get_markbase_root();
|
||||||
|
format!("mkdir -p {}/data && cd {} && nohup ./target/release/markbase-core web-start --port 11438 > /tmp/markbase_web.log 2>&1 &", markbase_root, markbase_root)
|
||||||
}
|
}
|
||||||
"Core API" | "core" => {
|
"Core API" | "core" => {
|
||||||
"cd /Users/accusys/momentry_core && nohup bash run-server-3002.sh > /tmp/momentry_start.log 2>&1 &"
|
let core_root = get_momentry_core_root();
|
||||||
|
format!("cd {} && nohup bash run-server-3002.sh > /tmp/momentry_start.log 2>&1 &", core_root)
|
||||||
}
|
}
|
||||||
"Proxy" | "proxy" => {
|
"Proxy" | "proxy" => {
|
||||||
"cd /Users/accusys/momentry_studio/src-tauri && nohup ./target/debug/momentry-proxy > /tmp/momentry_proxy.log 2>&1 &"
|
let studio_root = get_studio_root();
|
||||||
|
format!("cd {}/src-tauri && nohup ./target/debug/momentry-proxy > /tmp/momentry_proxy.log 2>&1 &", studio_root)
|
||||||
}
|
}
|
||||||
"MarkBaseEngine" | "mbe" => {
|
"MarkBaseEngine" | "mbe" => {
|
||||||
"cd /Users/accusys/MarkBaseEngine && nohup .build/debug/MarkBaseServer > /tmp/markbase_engine.log 2>&1 &"
|
let engine_root = get_markbase_engine_root();
|
||||||
|
format!("cd {} && nohup .build/debug/MarkBaseServer > /tmp/markbase_engine.log 2>&1 &", engine_root)
|
||||||
}
|
}
|
||||||
"SFTP" => {
|
"SFTP" => {
|
||||||
"cd /Users/accusys/markbase && nohup cargo run --bin markbase-core -- ssh-start --port 2024 > /tmp/markbase_ssh.log 2>&1 &"
|
let markbase_root = get_markbase_root();
|
||||||
|
format!("cd {} && nohup cargo run --bin markbase-core -- ssh-start --port 2024 > /tmp/markbase_ssh.log 2>&1 &", markbase_root)
|
||||||
}
|
}
|
||||||
"SMB" => {
|
"SMB" => {
|
||||||
"cd /Users/accusys/markbase && nohup cargo run --bin markbase-core --features smb-server -- smb-start --port 4445 --share-name markbase --root /Users/accusys/momentry/var/sftpgo/data --user demo:demo123 > /tmp/markbase_smb.log 2>&1 &"
|
let markbase_root = get_markbase_root();
|
||||||
|
let storage_root = get_storage_root();
|
||||||
|
format!("cd {} && nohup cargo run --bin markbase-core --features smb-server -- smb-start --port 4445 --share-name markbase --root {} --user demo:demo123 > /tmp/markbase_smb.log 2>&1 &", markbase_root, storage_root)
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Unknown service: {}", name)),
|
_ => return Err(format!("Unknown service: {}", name)),
|
||||||
};
|
};
|
||||||
@@ -1476,7 +1506,7 @@ async fn get_current_user(username: String) -> Result<Option<db::User>, String>
|
|||||||
|
|
||||||
#[tauri::command(rename_all = "camelCase")]
|
#[tauri::command(rename_all = "camelCase")]
|
||||||
async fn list_client_files(path: String) -> Result<serde_json::Value, String> {
|
async fn list_client_files(path: String) -> Result<serde_json::Value, String> {
|
||||||
let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("/Users/accusys"));
|
let home = get_home_dir();
|
||||||
let root = home.join("momentry/var/sftpgo/data/demo");
|
let root = home.join("momentry/var/sftpgo/data/demo");
|
||||||
let full_path = if path.is_empty() || path == "/" { root.clone() } else { root.join(&path) };
|
let full_path = if path.is_empty() || path == "/" { root.clone() } else { root.join(&path) };
|
||||||
|
|
||||||
@@ -1501,7 +1531,7 @@ async fn list_client_files(path: String) -> Result<serde_json::Value, String> {
|
|||||||
|
|
||||||
#[tauri::command(rename_all = "camelCase")]
|
#[tauri::command(rename_all = "camelCase")]
|
||||||
async fn mkdir_client(path: String) -> Result<(), String> {
|
async fn mkdir_client(path: String) -> Result<(), String> {
|
||||||
let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("/Users/accusys"));
|
let home = get_home_dir();
|
||||||
let root = home.join("momentry/var/sftpgo/data/demo");
|
let root = home.join("momentry/var/sftpgo/data/demo");
|
||||||
let full_path = root.join(&path);
|
let full_path = root.join(&path);
|
||||||
|
|
||||||
@@ -1513,7 +1543,7 @@ async fn mkdir_client(path: String) -> Result<(), String> {
|
|||||||
|
|
||||||
#[tauri::command(rename_all = "camelCase")]
|
#[tauri::command(rename_all = "camelCase")]
|
||||||
async fn rm_client(path: String) -> Result<(), String> {
|
async fn rm_client(path: String) -> Result<(), String> {
|
||||||
let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("/Users/accusys"));
|
let home = get_home_dir();
|
||||||
let root = home.join("momentry/var/sftpgo/data/demo");
|
let root = home.join("momentry/var/sftpgo/data/demo");
|
||||||
let full_path = root.join(&path);
|
let full_path = root.join(&path);
|
||||||
|
|
||||||
@@ -1542,13 +1572,11 @@ fn main() {
|
|||||||
|
|
||||||
if let Ok(output) = check {
|
if let Ok(output) = check {
|
||||||
if output.stdout.is_empty() {
|
if output.stdout.is_empty() {
|
||||||
let cmd = "mkdir -p /Users/accusys/markbase/data && \
|
let markbase_root = get_markbase_root();
|
||||||
cd /Users/accusys/markbase && \
|
let cmd = format!("mkdir -p {}/data && cd {} && nohup ./target/release/markbase-core web-start --port 11438 > /tmp/markbase_web.log 2>&1 &", markbase_root, markbase_root);
|
||||||
nohup ./target/release/markbase-core web-start --port 11438 \
|
|
||||||
> /tmp/markbase_web.log 2>&1 &";
|
|
||||||
let _ = std::process::Command::new("sh")
|
let _ = std::process::Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(cmd)
|
.arg(&cmd)
|
||||||
.spawn();
|
.spawn();
|
||||||
eprintln!("[startup] MarkBase started on port 11438");
|
eprintln!("[startup] MarkBase started on port 11438");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
101
src-tauri/src/paths.rs
Normal file
101
src-tauri/src/paths.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// Helper functions to get directory paths dynamically
|
||||||
|
// Priority: Environment Variable > User Home Directory + Standard Path
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// Get user home directory
|
||||||
|
pub fn get_home_dir() -> PathBuf {
|
||||||
|
dirs::home_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get Momentry Core root directory
|
||||||
|
/// Priority: MOMENTRY_CORE_ROOT env var > ~/momentry_core
|
||||||
|
pub fn get_momentry_core_root() -> String {
|
||||||
|
env::var("MOMENTRY_CORE_ROOT")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
format!("{}/momentry_core", get_home_dir().display())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get MarkBase root directory
|
||||||
|
/// Priority: MARKBASE_ROOT env var > ~/markbase
|
||||||
|
pub fn get_markbase_root() -> String {
|
||||||
|
env::var("MARKBASE_ROOT")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
format!("{}/markbase", get_home_dir().display())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get Studio root directory
|
||||||
|
/// Priority: MOMENTRY_STUDIO_ROOT env var > ~/momentry_studio
|
||||||
|
pub fn get_studio_root() -> String {
|
||||||
|
env::var("MOMENTRY_STUDIO_ROOT")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
format!("{}/momentry_studio", get_home_dir().display())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get Momentry root directory
|
||||||
|
/// Priority: MOMENTRY_ROOT env var > ~/momentry
|
||||||
|
pub fn get_momentry_root() -> String {
|
||||||
|
env::var("MOMENTRY_ROOT")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
format!("{}/momentry", get_home_dir().display())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get storage root directory
|
||||||
|
/// Priority: STORAGE_ROOT env var > ~/momentry/var/sftpgo/data
|
||||||
|
pub fn get_storage_root() -> String {
|
||||||
|
env::var("STORAGE_ROOT")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
format!("{}/momentry/var/sftpgo/data", get_home_dir().display())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get MarkBaseEngine root directory
|
||||||
|
/// Priority: MARKBASE_ENGINE_ROOT env var > ~/MarkBaseEngine
|
||||||
|
pub fn get_markbase_engine_root() -> String {
|
||||||
|
env::var("MARKBASE_ENGINE_ROOT")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
format!("{}/MarkBaseEngine", get_home_dir().display())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get worker PID file path
|
||||||
|
pub fn get_worker_pid_file() -> String {
|
||||||
|
format!("{}/logs/worker_3002.pid", get_momentry_core_root())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get worker log file path
|
||||||
|
pub fn get_worker_log_file() -> String {
|
||||||
|
format!("{}/logs/worker_3002.log", get_momentry_core_root())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod path_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_home_dir() {
|
||||||
|
let home = get_home_dir();
|
||||||
|
assert!(home.exists() || home.to_str() == Some("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_momentry_core_root_without_env() {
|
||||||
|
env::remove_var("MOMENTRY_CORE_ROOT");
|
||||||
|
let root = get_momentry_core_root();
|
||||||
|
assert!(root.contains("momentry_core"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_momentry_core_root_with_env() {
|
||||||
|
env::set_var("MOMENTRY_CORE_ROOT", "/custom/path/core");
|
||||||
|
let root = get_momentry_core_root();
|
||||||
|
assert_eq!(root, "/custom/path/core");
|
||||||
|
env::remove_var("MOMENTRY_CORE_ROOT");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user