Compare commits

..

5 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] fcf0ee619b Improve URL pattern matching and remove redundant checks
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
2025-12-27 08:51:12 +00:00
copilot-swe-agent[bot] f3ec352066 Address code review feedback: improve pattern matching and error handling
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
2025-12-27 08:49:41 +00:00
copilot-swe-agent[bot] 023d0f1cc4 Fix pyproject.toml path handling and add error handling
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
2025-12-27 08:46:12 +00:00
copilot-swe-agent[bot] f526d3f972 Add support for GitHub URLs and requirements files in Python dependencies
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
2025-12-27 08:41:43 +00:00
copilot-swe-agent[bot] e28f746294 Initial plan 2025-12-27 08:35:03 +00:00
13 changed files with 202 additions and 87 deletions
+61 -4
View File
@@ -535,12 +535,43 @@ export async function setSystemTimezone(timezone: string): Promise<boolean> {
}
}
// Helper function to check if a name is a GitHub URL
function isGitHubUrl(name: string): boolean {
// Support git+https://, git+http://, https://, and http:// URLs
// This covers GitHub URLs and other git-compatible repositories
return !!name.match(/^(git\+https?:\/\/|https?:\/\/)/i);
}
// Helper function to check if a name is a requirements file
function isRequirementsFile(name: string): boolean {
return !!name.match(/requirements.*\.(txt|in)$/i);
}
// Helper function to check if a name is a pyproject.toml file
function isPyprojectToml(name: string): boolean {
return name.endsWith('pyproject.toml');
}
export function getGetCommand(type: DependenceTypes, name: string): string {
const trimmedName = name.trim();
// For Python dependencies installed from GitHub or requirements files,
// we can't reliably check if they're installed, so skip the check
if (type === DependenceTypes.python3) {
if (isGitHubUrl(trimmedName) ||
isRequirementsFile(trimmedName) ||
isPyprojectToml(trimmedName)) {
// Return a command that will always indicate not installed
// This ensures GitHub URLs and requirements files are always installed
return 'echo ""';
}
}
const baseCommands = {
[DependenceTypes.nodejs]: `pnpm ls -g | grep "${name}" | head -1`,
[DependenceTypes.nodejs]: `pnpm ls -g | grep "${trimmedName}" | head -1`,
[DependenceTypes.python3]: `
python3 -c "exec('''
name='${name}'
name='${trimmedName}'
try:
from importlib.metadata import version
print(version(name))
@@ -550,7 +581,7 @@ except:
spec=u.find_spec(name)
print(name if spec else '')
''')"`,
[DependenceTypes.linux]: `apk info -es ${name}`,
[DependenceTypes.linux]: `apk info -es ${trimmedName}`,
};
return baseCommands[type];
@@ -570,7 +601,33 @@ export function getInstallCommand(type: DependenceTypes, name: string): string {
command = `${command} --prefix=${PYTHON_INSTALL_DIR}`;
}
return `${command} ${name.trim()}`;
const trimmedName = name.trim();
// Handle different installation methods for Python
if (type === DependenceTypes.python3) {
// Check if it's a GitHub URL (support both git+ and direct URLs)
if (isGitHubUrl(trimmedName)) {
return `${command} ${trimmedName}`;
}
// Check if it's a requirements file path
if (isRequirementsFile(trimmedName)) {
return `${command} -r ${trimmedName}`;
}
// Check if it's a pyproject.toml file
if (isPyprojectToml(trimmedName)) {
// For pyproject.toml, install from the directory containing it
const pathMatch = trimmedName.match(/^(.+)\/pyproject\.toml$/);
if (pathMatch) {
// Has a path prefix, use the directory
return `${command} ${pathMatch[1]}`;
} else {
// Just "pyproject.toml", install current directory
return `${command} .`;
}
}
}
return `${command} ${trimmedName}`;
}
export function getUninstallCommand(
+5 -34
View File
@@ -13,29 +13,9 @@ import { isValidToken } from '../shared/auth';
import path from 'path';
export default ({ app }: { app: Application }) => {
// Security: Enable strict routing to prevent case-insensitive path bypass
app.set('case sensitive routing', true);
app.set('strict routing', true);
app.set('trust proxy', 'loopback');
app.use(cors());
// Security: Path normalization middleware to prevent case variation attacks
app.use((req, res, next) => {
const originalPath = req.path;
const normalizedPath = originalPath.toLowerCase();
// Block requests with case variations on protected paths
if (originalPath !== normalizedPath &&
(normalizedPath.startsWith('/api/') || normalizedPath.startsWith('/open/'))) {
return res.status(400).json({
code: 400,
message: 'Invalid path format'
});
}
next();
});
// Rewrite URLs to strip baseUrl prefix if configured
// This allows the rest of the app to work without baseUrl awareness
if (config.baseUrl) {
@@ -56,7 +36,7 @@ export default ({ app }: { app: Application }) => {
secret: config.jwt.secret,
algorithms: ['HS384'],
}).unless({
path: [...config.apiWhiteList, /^(\/(?!api\/).*)$/i],
path: [...config.apiWhiteList, /^\/(?!api\/).*/],
}),
);
@@ -71,20 +51,19 @@ export default ({ app }: { app: Application }) => {
});
app.use(async (req: Request, res, next) => {
const pathLower = req.path.toLowerCase();
if (!['/open/', '/api/'].some((x) => pathLower.startsWith(x))) {
if (!['/open/', '/api/'].some((x) => req.path.startsWith(x))) {
return next();
}
const headerToken = getToken(req);
if (pathLower.startsWith('/open/')) {
if (req.path.startsWith('/open/')) {
const apps = await shareStore.getApps();
const doc = apps?.filter((x) =>
x.tokens?.find((y) => y.value === headerToken),
)?.[0];
if (doc && doc.tokens && doc.tokens.length > 0) {
const currentToken = doc.tokens.find((x) => x.value === headerToken);
const keyMatch = pathLower.match(/\/open\/([a-z]+)\/*/);
const keyMatch = req.path.match(/\/open\/([a-z]+)\/*/);
const key = keyMatch && keyMatch[1];
if (
doc.scopes.includes(key as any) &&
@@ -119,15 +98,7 @@ export default ({ app }: { app: Application }) => {
});
app.use(async (req, res, next) => {
const pathLower = req.path.toLowerCase();
if (
![
'/api/user/init',
'/api/user/notification/init',
'/open/user/init',
'/open/user/notification/init',
].includes(req.path)
) {
if (!['/api/user/init', '/api/user/notification/init'].includes(req.path)) {
return next();
}
const authInfo =
+2 -2
View File
@@ -13,7 +13,7 @@ import { AuthDataType, SystemModel } from '../data/system';
import SystemService from '../services/system';
import UserService from '../services/user';
import { writeFile, readFile } from 'fs/promises';
import { createRandomString, fileExist, isDemoEnv, safeJSONParse } from '../config/util';
import { createRandomString, fileExist, safeJSONParse } from '../config/util';
import OpenService from '../services/open';
import { shareStore } from '../shared/store';
import Logger from './logger';
@@ -50,7 +50,7 @@ export default async () => {
const [authConfig] = await SystemModel.findOrCreate({
where: { type: AuthDataType.authConfig },
});
if (!authConfig?.info || isDemoEnv()) {
if (!authConfig?.info) {
let authInfo = {
username: 'admin',
password: 'admin',
+2 -3
View File
@@ -69,10 +69,9 @@ RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3 \
HOME=/root
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin:${HOME}/bin \
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin \
NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules:${PNPM_HOME}/global/5/node_modules \
PIP_CACHE_DIR=${PYTHON_HOME}/pip \
PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages
+2 -3
View File
@@ -69,10 +69,9 @@ RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3 \
HOME=/root
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin:${HOME}/bin \
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin \
NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules:${PNPM_HOME}/global/5/node_modules \
PIP_CACHE_DIR=${PYTHON_HOME}/pip \
PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages
+2
View File
@@ -1,5 +1,7 @@
#!/bin/bash
export PATH="$HOME/bin:$PATH"
dir_shell=/ql/shell
. $dir_shell/share.sh
+2 -2
View File
@@ -77,7 +77,7 @@
"js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"multer": "2.1.1",
"multer": "1.4.5-lts.1",
"node-schedule": "^2.1.0",
"nodemailer": "^6.9.16",
"p-queue-cjs": "7.3.4",
@@ -118,7 +118,7 @@
"@types/js-yaml": "^4.0.5",
"@types/jsonwebtoken": "^8.5.8",
"@types/lodash": "^4.14.185",
"@types/multer": "^2.0.0",
"@types/multer": "^1.4.7",
"@types/node": "^17.0.21",
"@types/node-schedule": "^1.3.2",
"@types/nodemailer": "^6.4.4",
+22 -30
View File
@@ -1,5 +1,9 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
sqlite3: git+https://github.com/whyour/node-sqlite3.git#v1.0.3
@@ -86,8 +90,8 @@ dependencies:
specifier: ^4.17.21
version: 4.17.21
multer:
specifier: 2.1.1
version: 2.1.1
specifier: 1.4.5-lts.1
version: 1.4.5-lts.1
node-schedule:
specifier: ^2.1.0
version: 2.1.1
@@ -190,8 +194,8 @@ devDependencies:
specifier: ^4.14.185
version: 4.17.13
'@types/multer':
specifier: ^2.0.0
version: 2.0.0
specifier: ^1.4.7
version: 1.4.12
'@types/node':
specifier: ^17.0.21
version: 17.0.45
@@ -3956,8 +3960,8 @@ packages:
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
dev: false
/@types/multer@2.0.0:
resolution: {integrity: sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==}
/@types/multer@1.4.12:
resolution: {integrity: sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==}
dependencies:
'@types/express': 4.17.21
dev: true
@@ -6329,13 +6333,13 @@ packages:
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
/concat-stream@2.0.0:
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
engines: {'0': node >= 6.0}
/concat-stream@1.6.2:
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
engines: {'0': node >= 0.8}
dependencies:
buffer-from: 1.1.2
inherits: 2.0.4
readable-stream: 3.6.2
readable-stream: 2.3.8
typedarray: 0.0.6
dev: false
@@ -6432,7 +6436,6 @@ packages:
/core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: true
/cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
@@ -8285,7 +8288,6 @@ packages:
/glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
dependencies:
foreground-child: 3.3.0
@@ -8298,7 +8300,7 @@ packages:
/glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
deprecated: Glob versions prior to v9 are no longer supported
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
@@ -9169,7 +9171,6 @@ packages:
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: true
/isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@@ -10036,7 +10037,6 @@ packages:
/minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: true
/minipass-collect@1.0.2:
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
@@ -10117,7 +10117,6 @@ packages:
hasBin: true
dependencies:
minimist: 1.2.8
dev: true
/mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
@@ -10152,14 +10151,17 @@ packages:
/ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
/multer@2.1.1:
resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==}
engines: {node: '>= 10.16.0'}
/multer@1.4.5-lts.1:
resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==}
engines: {node: '>= 6.0.0'}
dependencies:
append-field: 1.0.0
busboy: 1.6.0
concat-stream: 2.0.0
concat-stream: 1.6.2
mkdirp: 0.5.6
object-assign: 4.1.1
type-is: 1.6.18
xtend: 4.0.2
dev: false
/mz@2.7.0:
@@ -11528,7 +11530,6 @@ packages:
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: true
/process-okam@0.11.10:
resolution: {integrity: sha512-p8e5nl6/OCeMalVb9dSojND5B9m/nq64WsyUfRmrTdLMKcNYcDN++/2I8WV1mTQDqrh2PQ6tIIb2A7/A38eSvw==}
@@ -12950,7 +12951,6 @@ packages:
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
dev: true
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
@@ -13221,7 +13221,6 @@ packages:
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
dev: true
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -13869,7 +13868,6 @@ packages:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
dependencies:
safe-buffer: 5.1.2
dev: true
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
@@ -14154,7 +14152,6 @@ packages:
/tar@6.2.1:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
dependencies:
chownr: 2.0.0
fs-minipass: 2.1.0
@@ -15143,7 +15140,6 @@ packages:
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
dev: true
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
@@ -15229,7 +15225,3 @@ packages:
- encoding
- supports-color
dev: false
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+1 -1
View File
@@ -92,7 +92,7 @@ run_normal() {
cd $dir_scripts
local relative_path="${file_param%/*}"
if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
if [[ ${file_param} != /* ]] && [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
cd ${relative_path}
file_param=${file_param/$relative_path\//}
fi
+1 -1
View File
@@ -42,7 +42,7 @@ def run():
os.environ["PYTHONPATH"] = prev_pythonpath
split_str = "__sitecustomize__"
file_name = os.path.abspath(sys.argv[0]).replace(f"{os.getenv('dir_scripts')}/", "")
file_name = sys.argv[0].replace(f"{os.getenv('dir_scripts')}/", "")
# 创建临时文件路径
temp_file = f"/tmp/env_{os.getpid()}.json"
+74
View File
@@ -111,6 +111,76 @@ add_cron() {
notify_api "$path 新增任务" "$detail"
}
## 自动安装订阅仓库中的Python依赖
auto_install_python_deps() {
local repo_path="$1"
local uniq_path="$2"
echo -e "\n检测订阅仓库中的Python依赖文件...\n"
get_token
# 检查 requirements.txt
if [[ -f "${repo_path}/requirements.txt" ]]; then
echo -e "发现 requirements.txt,开始自动安装依赖...\n"
local req_file="${dir_scripts}/${uniq_path}/requirements.txt"
# 确保目标目录存在
make_dir "${dir_scripts}/${uniq_path}"
# 复制文件并检查结果
if cp -f "${repo_path}/requirements.txt" "${req_file}" 2>/dev/null; then
# 调用API添加依赖安装任务
local dep_name="${uniq_path}/requirements.txt"
local currentTimeStamp=$(date +%s)
local result=$(curl -s --noproxy "*" "http://127.0.0.1:${ql_port}/open/dependencies?t=$currentTimeStamp" \
-X POST \
-H "Content-Type: application/json;charset=UTF-8" \
-H "Authorization: Bearer ${__ql_token__}" \
--data-raw "[{\"name\":\"${dep_name}\",\"type\":1,\"remark\":\"自动检测:${uniq_path} 订阅依赖\"}]" 2>/dev/null)
local code=$(echo "$result" | jq -r '.code' 2>/dev/null)
if [[ "$code" == "200" ]]; then
echo -e "已添加 requirements.txt 依赖安装任务\n"
else
echo -e "添加 requirements.txt 依赖失败,请手动添加\n"
fi
else
echo -e "复制 requirements.txt 失败,跳过自动安装\n"
fi
fi
# 检查 pyproject.toml
if [[ -f "${repo_path}/pyproject.toml" ]]; then
echo -e "发现 pyproject.toml,开始自动安装依赖...\n"
local pyproject_file="${dir_scripts}/${uniq_path}/pyproject.toml"
# 确保目标目录存在
make_dir "${dir_scripts}/${uniq_path}"
# 复制文件并检查结果
if cp -f "${repo_path}/pyproject.toml" "${pyproject_file}" 2>/dev/null; then
# 调用API添加依赖安装任务
local dep_name="${uniq_path}/pyproject.toml"
local currentTimeStamp=$(date +%s)
local result=$(curl -s --noproxy "*" "http://127.0.0.1:${ql_port}/open/dependencies?t=$currentTimeStamp" \
-X POST \
-H "Content-Type: application/json;charset=UTF-8" \
-H "Authorization: Bearer ${__ql_token__}" \
--data-raw "[{\"name\":\"${dep_name}\",\"type\":1,\"remark\":\"自动检测:${uniq_path} 订阅依赖\"}]" 2>/dev/null)
local code=$(echo "$result" | jq -r '.code' 2>/dev/null)
if [[ "$code" == "200" ]]; then
echo -e "已添加 pyproject.toml 依赖安装任务\n"
else
echo -e "添加 pyproject.toml 依赖失败,请手动添加\n"
fi
else
echo -e "复制 pyproject.toml 失败,跳过自动安装\n"
fi
fi
}
## 更新仓库
update_repo() {
local url="$1"
@@ -137,6 +207,10 @@ update_repo() {
if [[ $exit_status -eq 0 ]]; then
echo -e "拉取 ${uniq_path} 成功...\n"
# 自动检测并安装Python依赖
auto_install_python_deps "${repo_path}" "${uniq_path}"
diff_scripts "$repo_path" "$author" "$path" "$blackword" "$dependence" "$extensions" "$autoAddCron" "$autoDelCron"
else
echo -e "拉取 ${uniq_path} 失败,请检查日志...\n"
+18 -2
View File
@@ -22,6 +22,9 @@ const DependenceModal = ({
}) => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [selectedType, setSelectedType] = useState(
DependenceTypes[defaultType as any],
);
const handleOk = async (values: any) => {
setLoading(true);
@@ -90,7 +93,7 @@ const DependenceModal = ({
label={intl.get('依赖类型')}
initialValue={DependenceTypes[defaultType as any]}
>
<Select>
<Select onChange={(value) => setSelectedType(value)}>
{config.dependenceTypes.map((x, i) => (
<Option key={i} value={i}>
{x}
@@ -121,11 +124,24 @@ const DependenceModal = ({
whitespace: true,
},
]}
tooltip={
selectedType === DependenceTypes.python3
? intl.get(
'Python支持多种安装方式:\n1. 包名(如:requests\n2. GitHub链接(如:git+https://github.com/user/repo.git\n3. requirements文件路径(如:path/to/requirements.txt\n4. pyproject.toml文件路径',
)
: undefined
}
>
<Input.TextArea
rows={4}
autoSize={{ minRows: 1, maxRows: 5 }}
placeholder={intl.get('请输入依赖名称')}
placeholder={
selectedType === DependenceTypes.python3
? intl.get(
'支持包名、GitHub链接、requirements.txt或pyproject.toml路径',
)
: intl.get('请输入依赖名称')
}
/>
</Form.Item>
<Form.Item name="remark" label={intl.get('备注')}>
+10 -5
View File
@@ -1,6 +1,11 @@
version: 2.20.2
changeLogLink: https://t.me/jiao_long/434
publishTime: 2026-03-01 1800
version: 2.20.1
changeLogLink: https://t.me/jiao_long/433
publishTime: 2025-12-26 22:00
changeLog: |
1. 修复 path 安全漏洞(重要)
1. 修复获取依赖管理列表
2. notify.js 修复 TG_PROXY_AUTH 参数拼接
3. QLAPI.notify larkSecret 参数
4. 修复 cron parser 定时规则校验
5. 修复设置 baseUrl 后无法访问
6. 修复环境变量排序
7. 修复定时任务无法停止