Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f284e14630 | |||
| bff955e45b | |||
| 6f7ab6dbc6 |
2
build.sh
2
build.sh
@@ -4,7 +4,7 @@ cd "$(dirname "$0")/source_clean"
|
|||||||
|
|
||||||
VERSION=$(cat ../VERSION)
|
VERSION=$(cat ../VERSION)
|
||||||
echo "🔨 Building CloudSearch v${VERSION}..."
|
echo "🔨 Building CloudSearch v${VERSION}..."
|
||||||
|
cp ../VERSION ./VERSION
|
||||||
docker build -t cloudsearch-app:v${VERSION} -t cloudsearch-app:latest .
|
docker build -t cloudsearch-app:v${VERSION} -t cloudsearch-app:latest .
|
||||||
|
|
||||||
echo "✅ Built: cloudsearch-app:v${VERSION} + cloudsearch-app:latest"
|
echo "✅ Built: cloudsearch-app:v${VERSION} + cloudsearch-app:latest"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.4.0
|
0.4.6
|
||||||
|
|||||||
11
source_clean/build.sh
Executable file
11
source_clean/build.sh
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$0")/source_clean"
|
||||||
|
|
||||||
|
VERSION=$(cat ../VERSION)
|
||||||
|
echo "🔨 Building CloudSearch v${VERSION}..."
|
||||||
|
|
||||||
|
docker build -t cloudsearch-app:v${VERSION} -t cloudsearch-app:latest .
|
||||||
|
|
||||||
|
echo "✅ Built: cloudsearch-app:v${VERSION} + cloudsearch-app:latest"
|
||||||
|
echo " Run: docker-compose up -d app"
|
||||||
@@ -7,7 +7,10 @@ cd "$SCRIPT_DIR"
|
|||||||
# 如果 docker-compose.yml 不存在,自动下载
|
# 如果 docker-compose.yml 不存在,自动下载
|
||||||
if [ ! -f docker-compose.yml ]; then
|
if [ ! -f docker-compose.yml ]; then
|
||||||
echo "📥 下载 docker-compose.yml..."
|
echo "📥 下载 docker-compose.yml..."
|
||||||
wget -q https://gitea.timxx.cn/admin/CloudSearch/raw/branch/master/source_clean/docker-compose.yml
|
wget -q https://gitea.timxx.cn/admin/CloudSearch/raw/branch/master/source_clean/docker-compose.yml || {
|
||||||
|
echo "❌ 下载失败,请检查网络"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "🔍 检测 Redis..."
|
echo "🔍 检测 Redis..."
|
||||||
@@ -23,11 +26,43 @@ if [ -n "$EXISTING_REDIS" ]; then
|
|||||||
echo " ✅ 已加入 cloudsearch-net"
|
echo " ✅ 已加入 cloudsearch-net"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 检测 Redis 密码
|
# 检测 Redis 密码 (多种来源)
|
||||||
REDIS_PASS=$(docker inspect "$EXISTING_REDIS" --format '{{range .Config.Cmd}}{{println .}}{{end}}' 2>/dev/null | grep -A1 'requirepass' | tail -1 || true)
|
REDIS_PASS=""
|
||||||
|
|
||||||
|
# 方法1: 从命令行参数 --requirepass
|
||||||
|
PASS_FROM_CMD=$(docker inspect "$EXISTING_REDIS" --format '{{range .Config.Cmd}}{{println .}}{{end}}' 2>/dev/null | grep -A1 'requirepass' | tail -1 | tr -d '[:space:]' || true)
|
||||||
|
if [ -n "$PASS_FROM_CMD" ] && [ "$PASS_FROM_CMD" != "redis-server" ] && [ "$PASS_FROM_CMD" != "/etc/redis/redis.conf" ]; then
|
||||||
|
REDIS_PASS="$PASS_FROM_CMD"
|
||||||
|
echo " 🔑 从启动参数检测到 Redis 密码"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 方法2: 从 redis.conf 读取 (1Panel 常见配置方式)
|
||||||
|
if [ -z "$REDIS_PASS" ]; then
|
||||||
|
PASS_FROM_CONF=$(docker exec "$EXISTING_REDIS" cat /etc/redis/redis.conf 2>/dev/null | grep '^requirepass ' | awk '{print $2}' | tr -d '"' || true)
|
||||||
|
if [ -n "$PASS_FROM_CONF" ]; then
|
||||||
|
REDIS_PASS="$PASS_FROM_CONF"
|
||||||
|
echo " 🔑 从 redis.conf 检测到 Redis 密码"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 方法3: 尝试无密码连接测试
|
||||||
|
if [ -z "$REDIS_PASS" ]; then
|
||||||
|
if docker exec "$EXISTING_REDIS" redis-cli ping 2>/dev/null | grep -q PONG; then
|
||||||
|
echo " ℹ️ Redis 无密码(已通过连接测试验证)"
|
||||||
|
else
|
||||||
|
# 有密码但检测不到 — 尝试从 redis-cli 的错误消息中提取
|
||||||
|
AUTH_ERR=$(docker exec "$EXISTING_REDIS" redis-cli ping 2>&1 || true)
|
||||||
|
if echo "$AUTH_ERR" | grep -q "NOAUTH\|AUTH"; then
|
||||||
|
echo " ⚠️ Redis 需要密码但无法自动检测,请手动设置 REDIS_URL"
|
||||||
|
echo " 提示: 在 .env 中设置 REDIS_URL=redis://:密码@${EXISTING_REDIS}:6379"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "$REDIS_PASS" ]; then
|
if [ -n "$REDIS_PASS" ]; then
|
||||||
REDIS_URL="redis://:${REDIS_PASS}@${EXISTING_REDIS}:6379"
|
# 对密码中的特殊字符进行URL编码
|
||||||
echo " 🔑 检测到 Redis 密码,已自动配置"
|
ENCODED_PASS=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$REDIS_PASS', safe=''))" 2>/dev/null || echo "$REDIS_PASS")
|
||||||
|
REDIS_URL="redis://:${ENCODED_PASS}@${EXISTING_REDIS}:6379"
|
||||||
else
|
else
|
||||||
REDIS_URL="redis://${EXISTING_REDIS}:6379"
|
REDIS_URL="redis://${EXISTING_REDIS}:6379"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -222,7 +222,8 @@ export async function testCloudConnection(id: number): Promise<{
|
|||||||
return { success: false, message: 'Cookie not configured' };
|
return { success: false, message: 'Cookie not configured' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const cookie = decryptCookie(config.cookie);
|
// config.cookie is already decrypted by getCloudConfigById
|
||||||
|
const cookie = config.cookie;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let valid = false;
|
let valid = false;
|
||||||
|
|||||||
@@ -309,6 +309,7 @@ function seedSystemConfigs(db: Database.Database): void {
|
|||||||
{ key: 'site_marquee', value: '📢 欢迎使用CloudSearch,所有资源仅供学习交流,请于下载后24小时内删除', description: '搜索栏下方滚动通知文字(从右往左滚动显示)' },
|
{ key: 'site_marquee', value: '📢 欢迎使用CloudSearch,所有资源仅供学习交流,请于下载后24小时内删除', description: '搜索栏下方滚动通知文字(从右往左滚动显示)' },
|
||||||
{ key: 'tmdb_api_token', value: '', description: 'TMDB API 读取令牌(用于增强豆瓣内容信息)' },
|
{ key: 'tmdb_api_token', value: '', description: 'TMDB API 读取令牌(用于增强豆瓣内容信息)' },
|
||||||
{ key: 'ip_geo_api_url', value: '', description: 'IP 归属地查询接口({ip} 会被替换为实际IP,留空则禁用)' },
|
{ key: 'ip_geo_api_url', value: '', description: 'IP 归属地查询接口({ip} 会被替换为实际IP,留空则禁用)' },
|
||||||
|
{ key: 'ip_geo_api_id', value: '', description: 'IP 归属地 API ID(apihz.cn 接口)' },
|
||||||
{ key: 'ip_geo_api_key', value: '', description: 'IP 归属地备用 API Key(留空使用默认)' },
|
{ key: 'ip_geo_api_key', value: '', description: 'IP 归属地备用 API Key(留空使用默认)' },
|
||||||
{ key: 'title_filter_rules', value: '', description: '搜索结果标题过滤规则(一行一条:纯文本直接移除 / 正则用/包围/)' },
|
{ key: 'title_filter_rules', value: '', description: '搜索结果标题过滤规则(一行一条:纯文本直接移除 / 正则用/包围/)' },
|
||||||
{ key: 'timezone', value: 'Asia/Shanghai', description: '系统时区(如 Asia/Shanghai、America/New_York、UTC)' },
|
{ key: 'timezone', value: 'Asia/Shanghai', description: '系统时区(如 Asia/Shanghai、America/New_York、UTC)' },
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ router.get('/admin/cloud-configs', (_req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** POST /api/admin/cloud-configs — create or smart-replace a cloud config */
|
/** POST /api/admin/cloud-configs — create or smart-replace a cloud config */
|
||||||
router.post('/admin/cloud-configs', (req: Request, res: Response) => {
|
router.post('/admin/cloud-configs', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const data = req.body;
|
const data = req.body;
|
||||||
if (!data.cloud_type) {
|
if (!data.cloud_type) {
|
||||||
@@ -164,6 +164,24 @@ router.post('/admin/cloud-configs', (req: Request, res: Response) => {
|
|||||||
// Normalize is_active: frontend sends boolean, SQLite needs 0/1
|
// Normalize is_active: frontend sends boolean, SQLite needs 0/1
|
||||||
if (typeof data.is_active === 'boolean') data.is_active = data.is_active ? 1 : 0;
|
if (typeof data.is_active === 'boolean') data.is_active = data.is_active ? 1 : 0;
|
||||||
const saved = saveCloudConfig(data);
|
const saved = saveCloudConfig(data);
|
||||||
|
|
||||||
|
// Auto-validate if cookie was provided (best-effort, non-blocking)
|
||||||
|
if (data.cookie && saved.id) {
|
||||||
|
try {
|
||||||
|
const result = await testCloudConnectionWithCookie(data.cloud_type, data.cookie);
|
||||||
|
if (result.success) {
|
||||||
|
const updateData: any = { id: saved.id, cloud_type: data.cloud_type };
|
||||||
|
if (result.nickname) updateData.nickname = result.nickname;
|
||||||
|
if (result.storage_used) updateData.storage_used = result.storage_used;
|
||||||
|
if (result.storage_total) updateData.storage_total = result.storage_total;
|
||||||
|
saveCloudConfig(updateData);
|
||||||
|
Object.assign(saved, { nickname: result.nickname, storage_used: result.storage_used, storage_total: result.storage_total });
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Auto-validation is best-effort, don't fail the save
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.json(saved);
|
res.json(saved);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(500).json({ error: err.message || 'Failed to save cloud config' });
|
res.status(500).json({ error: err.message || 'Failed to save cloud config' });
|
||||||
@@ -508,7 +526,7 @@ router.post('/admin/test-external-service', async (req: Request, res: Response)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'tmdb': {
|
case 'tmdb': {
|
||||||
const tmdbToken = token || getSystemConfig('tmdb_api_key') || '';
|
const tmdbToken = token || getSystemConfig('tmdb_api_token') || '';
|
||||||
if (!tmdbToken) {
|
if (!tmdbToken) {
|
||||||
res.json({ ok: false, info: 'TMDB API Key not configured' });
|
res.json({ ok: false, info: 'TMDB API Key not configured' });
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user