jettask 0.2.9__py3-none-any.whl → 0.2.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jettask/core/cli.py +139 -88
- jettask/webui/backend/namespace_data_access.py +5 -1
- jettask/webui/frontend/src/main.jsx +2 -0
- jettask/webui/frontend/src/pages/ScheduledTasks.jsx +2 -1
- jettask/webui/frontend/src/pages/Settings.jsx +2 -1
- jettask/webui/frontend/src/services/api.js +49 -4
- jettask/webui/frontend/src/services/queueTrend.js +16 -2
- {jettask-0.2.9.dist-info → jettask-0.2.14.dist-info}/METADATA +1 -1
- {jettask-0.2.9.dist-info → jettask-0.2.14.dist-info}/RECORD +13 -13
- {jettask-0.2.9.dist-info → jettask-0.2.14.dist-info}/WHEEL +0 -0
- {jettask-0.2.9.dist-info → jettask-0.2.14.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.9.dist-info → jettask-0.2.14.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.9.dist-info → jettask-0.2.14.dist-info}/top_level.txt +0 -0
jettask/core/cli.py
CHANGED
@@ -477,16 +477,17 @@ def scheduler(task_center, interval, batch_size, check_interval, debug):
|
|
477
477
|
@cli.command()
|
478
478
|
@click.option('--port', default=8080, help='前端服务器端口')
|
479
479
|
@click.option('--host', default='0.0.0.0', help='前端服务器监听地址')
|
480
|
+
@click.option('--api-url', default='http://localhost:8001', help='后端 API 服务器地址')
|
480
481
|
@click.option('--auto-install', is_flag=True, default=True, help='自动安装缺失的依赖')
|
481
482
|
@click.option('--force-install', is_flag=True, help='强制重新安装依赖')
|
482
483
|
@click.option('--build-only', is_flag=True, help='仅构建生产版本,不启动服务器')
|
483
|
-
def frontend(port, host, auto_install, force_install, build_only):
|
484
|
+
def frontend(port, host, api_url, auto_install, force_install, build_only):
|
484
485
|
"""启动 WebUI 前端界面
|
485
486
|
|
486
487
|
启动生产版本服务器:
|
487
488
|
1. 检查 Node.js 和 npm 是否安装
|
488
|
-
2.
|
489
|
-
3.
|
489
|
+
2. 在包目录安装依赖并构建
|
490
|
+
3. 启动生产版本服务器
|
490
491
|
|
491
492
|
示例:
|
492
493
|
# 启动生产版本服务器(默认)
|
@@ -495,6 +496,9 @@ def frontend(port, host, auto_install, force_install, build_only):
|
|
495
496
|
# 指定端口
|
496
497
|
jettask frontend --port 3000
|
497
498
|
|
499
|
+
# 指定后端API地址
|
500
|
+
jettask frontend --api-url http://192.168.1.100:8001
|
501
|
+
|
498
502
|
# 仅构建生产版本
|
499
503
|
jettask frontend --build-only
|
500
504
|
|
@@ -505,79 +509,12 @@ def frontend(port, host, auto_install, force_install, build_only):
|
|
505
509
|
import shutil
|
506
510
|
from pathlib import Path
|
507
511
|
|
508
|
-
#
|
509
|
-
|
510
|
-
if not
|
511
|
-
click.echo(f"
|
512
|
+
# 获取前端目录路径
|
513
|
+
frontend_dir = Path(__file__).parent.parent / "webui" / "frontend"
|
514
|
+
if not frontend_dir.exists():
|
515
|
+
click.echo(f"错误:前端目录不存在: {frontend_dir}", err=True)
|
512
516
|
sys.exit(1)
|
513
517
|
|
514
|
-
# 获取用户工作目录(在用户主目录下)
|
515
|
-
user_home = Path.home()
|
516
|
-
user_jettask_dir = user_home / ".jettask"
|
517
|
-
user_frontend_dir = user_jettask_dir / "frontend"
|
518
|
-
|
519
|
-
# 确保用户目录存在
|
520
|
-
user_jettask_dir.mkdir(parents=True, exist_ok=True)
|
521
|
-
|
522
|
-
# 检查是否需要复制/更新前端代码
|
523
|
-
should_copy = False
|
524
|
-
if not user_frontend_dir.exists():
|
525
|
-
should_copy = True
|
526
|
-
click.echo(f"首次使用,正在初始化前端环境...")
|
527
|
-
else:
|
528
|
-
# 检查版本或时间戳来决定是否需要更新
|
529
|
-
source_package_json = source_frontend_dir / "package.json"
|
530
|
-
user_package_json = user_frontend_dir / "package.json"
|
531
|
-
if not user_package_json.exists():
|
532
|
-
should_copy = True
|
533
|
-
else:
|
534
|
-
# 比较修改时间
|
535
|
-
if source_package_json.stat().st_mtime > user_package_json.stat().st_mtime:
|
536
|
-
should_copy = True
|
537
|
-
click.echo("检测到前端代码更新,正在同步...")
|
538
|
-
|
539
|
-
if should_copy:
|
540
|
-
# 复制前端代码到用户目录
|
541
|
-
if user_frontend_dir.exists():
|
542
|
-
# 保留 node_modules 和 dist 目录
|
543
|
-
node_modules_backup = None
|
544
|
-
dist_backup = None
|
545
|
-
|
546
|
-
node_modules_dir = user_frontend_dir / "node_modules"
|
547
|
-
dist_dir = user_frontend_dir / "dist"
|
548
|
-
|
549
|
-
if node_modules_dir.exists() and not force_install:
|
550
|
-
# 备份 node_modules
|
551
|
-
node_modules_backup = user_jettask_dir / "node_modules_backup"
|
552
|
-
if node_modules_backup.exists():
|
553
|
-
shutil.rmtree(node_modules_backup)
|
554
|
-
shutil.move(str(node_modules_dir), str(node_modules_backup))
|
555
|
-
|
556
|
-
if dist_dir.exists():
|
557
|
-
# 备份 dist
|
558
|
-
dist_backup = user_jettask_dir / "dist_backup"
|
559
|
-
if dist_backup.exists():
|
560
|
-
shutil.rmtree(dist_backup)
|
561
|
-
shutil.move(str(dist_dir), str(dist_backup))
|
562
|
-
|
563
|
-
# 删除旧的前端目录
|
564
|
-
shutil.rmtree(user_frontend_dir)
|
565
|
-
|
566
|
-
# 复制新的前端代码
|
567
|
-
shutil.copytree(source_frontend_dir, user_frontend_dir, ignore=shutil.ignore_patterns('node_modules', 'dist', '.git'))
|
568
|
-
|
569
|
-
# 恢复备份
|
570
|
-
if 'node_modules_backup' in locals() and node_modules_backup and node_modules_backup.exists():
|
571
|
-
shutil.move(str(node_modules_backup), str(user_frontend_dir / "node_modules"))
|
572
|
-
|
573
|
-
if 'dist_backup' in locals() and dist_backup and dist_backup.exists():
|
574
|
-
shutil.move(str(dist_backup), str(user_frontend_dir / "dist"))
|
575
|
-
|
576
|
-
click.echo(f"✓ 前端代码已同步到: {user_frontend_dir}")
|
577
|
-
|
578
|
-
# 工作目录切换到用户前端目录
|
579
|
-
frontend_dir = user_frontend_dir
|
580
|
-
|
581
518
|
# 检查 Node.js 是否安装
|
582
519
|
node_cmd = shutil.which('node')
|
583
520
|
if not node_cmd:
|
@@ -608,7 +545,7 @@ def frontend(port, host, auto_install, force_install, build_only):
|
|
608
545
|
|
609
546
|
# 切换到前端目录
|
610
547
|
os.chdir(frontend_dir)
|
611
|
-
click.echo(f"
|
548
|
+
click.echo(f"前端目录: {frontend_dir}")
|
612
549
|
|
613
550
|
# 检查 package.json 是否存在
|
614
551
|
package_json = frontend_dir / "package.json"
|
@@ -651,7 +588,8 @@ def frontend(port, host, auto_install, force_install, build_only):
|
|
651
588
|
# 构建或启动
|
652
589
|
try:
|
653
590
|
# 构建生产版本
|
654
|
-
|
591
|
+
# 注意:根据 vite.config.js,构建输出到 ../static/dist
|
592
|
+
dist_dir = frontend_dir.parent / "static" / "dist"
|
655
593
|
|
656
594
|
# 检查是否需要构建
|
657
595
|
need_build = not dist_dir.exists() or build_only
|
@@ -681,35 +619,148 @@ def frontend(port, host, auto_install, force_install, build_only):
|
|
681
619
|
sys.exit(1)
|
682
620
|
|
683
621
|
click.echo(f"\n正在启动生产版本服务器...")
|
684
|
-
click.echo(f" -
|
685
|
-
|
686
|
-
|
622
|
+
click.echo(f" - 访问地址: http://localhost:{port}")
|
623
|
+
if host != 'localhost':
|
624
|
+
click.echo(f" - 网络地址: http://{host}:{port}")
|
625
|
+
click.echo(f" - 静态文件: {dist_dir}")
|
626
|
+
click.echo(f" - API 地址: {api_url}")
|
687
627
|
click.echo("\n按 Ctrl+C 停止服务器\n")
|
688
628
|
|
689
629
|
# 使用 Python 内置的 HTTP 服务器提供静态文件
|
690
630
|
import http.server
|
691
631
|
import socketserver
|
632
|
+
import urllib.request
|
633
|
+
import urllib.parse
|
634
|
+
import json
|
692
635
|
|
693
|
-
class
|
636
|
+
class ProxyHandler(http.server.SimpleHTTPRequestHandler):
|
694
637
|
def __init__(self, *args, **kwargs):
|
695
638
|
super().__init__(*args, directory=str(dist_dir), **kwargs)
|
696
639
|
|
640
|
+
def proxy_request(self, method='GET', body=None):
|
641
|
+
"""代理 API 请求到后端服务器"""
|
642
|
+
if self.path.startswith('/api/') or self.path.startswith('/ws/'):
|
643
|
+
# 构建目标 URL
|
644
|
+
target_url = api_url + self.path
|
645
|
+
|
646
|
+
try:
|
647
|
+
# 准备请求
|
648
|
+
req = urllib.request.Request(target_url, method=method)
|
649
|
+
|
650
|
+
# 复制请求头
|
651
|
+
for header, value in self.headers.items():
|
652
|
+
if header.lower() not in ['host', 'connection']:
|
653
|
+
req.add_header(header, value)
|
654
|
+
|
655
|
+
# 添加请求体
|
656
|
+
if body:
|
657
|
+
req.data = body
|
658
|
+
|
659
|
+
# 发送请求
|
660
|
+
with urllib.request.urlopen(req, timeout=30) as response:
|
661
|
+
# 返回响应
|
662
|
+
self.send_response(response.getcode())
|
663
|
+
for header, value in response.headers.items():
|
664
|
+
if header.lower() not in ['connection', 'transfer-encoding']:
|
665
|
+
self.send_header(header, value)
|
666
|
+
self.end_headers()
|
667
|
+
self.wfile.write(response.read())
|
668
|
+
return True
|
669
|
+
|
670
|
+
except urllib.error.HTTPError as e:
|
671
|
+
self.send_response(e.code)
|
672
|
+
self.end_headers()
|
673
|
+
self.wfile.write(e.read())
|
674
|
+
return True
|
675
|
+
except Exception as e:
|
676
|
+
self.send_error(502, f"Bad Gateway: {str(e)}")
|
677
|
+
return True
|
678
|
+
return False
|
679
|
+
|
697
680
|
def do_GET(self):
|
698
|
-
#
|
699
|
-
if self.
|
700
|
-
|
681
|
+
# 先尝试代理 API 请求
|
682
|
+
if self.proxy_request('GET'):
|
683
|
+
return
|
684
|
+
|
685
|
+
# 对于 index.html,注入 API URL
|
686
|
+
if self.path == '/' or self.path == '/index.html' or (
|
687
|
+
self.path != '/' and not Path(dist_dir / self.path[1:]).exists()
|
688
|
+
):
|
689
|
+
# 读取 index.html
|
690
|
+
index_path = dist_dir / 'index.html'
|
691
|
+
if index_path.exists():
|
692
|
+
with open(index_path, 'r', encoding='utf-8') as f:
|
693
|
+
html_content = f.read()
|
694
|
+
|
695
|
+
# 注入 API URL 配置(使用相对路径,因为我们会代理)
|
696
|
+
api_config = f'''
|
697
|
+
<script>
|
698
|
+
window.JETTASK_API_URL = ''; // 使用相对路径,由服务器代理
|
699
|
+
console.log('API requests will be proxied by server');
|
700
|
+
</script>
|
701
|
+
'''
|
702
|
+
|
703
|
+
# 在 </head> 标签前注入配置
|
704
|
+
html_content = html_content.replace('</head>', api_config + '</head>')
|
705
|
+
|
706
|
+
# 发送响应
|
707
|
+
self.send_response(200)
|
708
|
+
self.send_header('Content-type', 'text/html')
|
709
|
+
self.send_header('Content-Length', str(len(html_content.encode('utf-8'))))
|
710
|
+
self.end_headers()
|
711
|
+
self.wfile.write(html_content.encode('utf-8'))
|
712
|
+
return
|
713
|
+
|
714
|
+
# 其他文件正常处理
|
701
715
|
return super().do_GET()
|
702
716
|
|
717
|
+
def do_POST(self):
|
718
|
+
# 读取请求体
|
719
|
+
content_length = int(self.headers.get('Content-Length', 0))
|
720
|
+
body = self.rfile.read(content_length) if content_length > 0 else None
|
721
|
+
|
722
|
+
# 代理 POST 请求
|
723
|
+
if self.proxy_request('POST', body):
|
724
|
+
return
|
725
|
+
|
726
|
+
# 不支持的请求
|
727
|
+
self.send_error(405, "Method Not Allowed")
|
728
|
+
|
729
|
+
def do_PUT(self):
|
730
|
+
content_length = int(self.headers.get('Content-Length', 0))
|
731
|
+
body = self.rfile.read(content_length) if content_length > 0 else None
|
732
|
+
if self.proxy_request('PUT', body):
|
733
|
+
return
|
734
|
+
self.send_error(405, "Method Not Allowed")
|
735
|
+
|
736
|
+
def do_DELETE(self):
|
737
|
+
if self.proxy_request('DELETE'):
|
738
|
+
return
|
739
|
+
self.send_error(405, "Method Not Allowed")
|
740
|
+
|
703
741
|
def log_message(self, format, *args):
|
704
742
|
# 自定义日志格式
|
705
743
|
click.echo(f"[{self.log_date_time_string()}] {format % args}")
|
706
744
|
|
707
745
|
# 创建服务器
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
746
|
+
class ReuseAddrTCPServer(socketserver.TCPServer):
|
747
|
+
allow_reuse_address = True
|
748
|
+
allow_reuse_port = True
|
749
|
+
|
750
|
+
try:
|
751
|
+
click.echo(f"正在绑定到 {host}:{port}...")
|
752
|
+
httpd = ReuseAddrTCPServer((host, port), ProxyHandler)
|
753
|
+
click.echo(f"✓ 服务器已启动,监听 {host}:{port}")
|
754
|
+
click.echo(f"✓ API 请求将被代理到 {api_url}")
|
755
|
+
httpd.serve_forever()
|
756
|
+
except OSError as e:
|
757
|
+
if e.errno == 98: # Address already in use
|
758
|
+
click.echo(f"错误:端口 {port} 已被占用,请尝试其他端口", err=True)
|
759
|
+
else:
|
760
|
+
click.echo(f"错误:{e}", err=True)
|
761
|
+
sys.exit(1)
|
762
|
+
except KeyboardInterrupt:
|
763
|
+
pass
|
713
764
|
except subprocess.CalledProcessError as e:
|
714
765
|
click.echo(f"错误:命令执行失败 - {e}", err=True)
|
715
766
|
sys.exit(1)
|
@@ -181,7 +181,11 @@ class NamespaceDataAccessManager:
|
|
181
181
|
|
182
182
|
async def get_namespace_config(self, namespace_name: str) -> dict:
|
183
183
|
"""从任务中心API获取命名空间配置"""
|
184
|
-
|
184
|
+
# 使用127.0.0.1替代localhost,确保容器内能正确连接
|
185
|
+
base_url = self.task_center_base_url
|
186
|
+
if 'localhost' in base_url:
|
187
|
+
base_url = base_url.replace('localhost', '127.0.0.1')
|
188
|
+
url = f"{base_url}/api/namespaces/{namespace_name}"
|
185
189
|
|
186
190
|
try:
|
187
191
|
session = await self._get_session()
|
@@ -102,8 +102,9 @@ function ScheduledTasks() {
|
|
102
102
|
offset: (params.current - 1) * params.pageSize,
|
103
103
|
};
|
104
104
|
|
105
|
+
const apiBase = window.JETTASK_API_URL || 'http://localhost:8001';
|
105
106
|
const response = await axios.get(
|
106
|
-
|
107
|
+
`${apiBase}/api/data/scheduled-tasks/${currentNamespace}`,
|
107
108
|
{ params: requestParams }
|
108
109
|
);
|
109
110
|
|
@@ -161,7 +161,8 @@ function Settings() {
|
|
161
161
|
refreshNamespaceList(); // 触发全局命名空间列表刷新
|
162
162
|
|
163
163
|
// 显示连接URL
|
164
|
-
const
|
164
|
+
const apiBase = window.JETTASK_API_URL || 'http://localhost:8001';
|
165
|
+
const fullUrl = `${apiBase}${response.data.connection_url}`;
|
165
166
|
Modal.success({
|
166
167
|
title: '命名空间创建成功',
|
167
168
|
content: (
|
@@ -2,6 +2,17 @@ import axios from 'axios';
|
|
2
2
|
|
3
3
|
// 智能获取 API 基础 URL
|
4
4
|
const getApiBaseUrl = () => {
|
5
|
+
// 优先使用注入的 API URL
|
6
|
+
if (typeof window !== 'undefined' && window.JETTASK_API_URL !== undefined) {
|
7
|
+
const url = window.JETTASK_API_URL;
|
8
|
+
// 如果是空字符串,表示使用相对路径(代理模式)
|
9
|
+
if (url === '') {
|
10
|
+
return ''; // 使用相对路径,不加前缀
|
11
|
+
}
|
12
|
+
// 否则确保 URL 以 /api 结尾
|
13
|
+
return url.endsWith('/api') ? url : `${url}/api`;
|
14
|
+
}
|
15
|
+
|
5
16
|
// 在开发环境中使用 localhost
|
6
17
|
if (typeof window !== 'undefined') {
|
7
18
|
const hostname = window.location.hostname;
|
@@ -19,6 +30,19 @@ const getApiBaseUrl = () => {
|
|
19
30
|
|
20
31
|
// 智能获取 WebSocket URL
|
21
32
|
const getWsBaseUrl = () => {
|
33
|
+
// 优先使用注入的 API URL
|
34
|
+
if (typeof window !== 'undefined' && window.JETTASK_API_URL !== undefined) {
|
35
|
+
const url = window.JETTASK_API_URL;
|
36
|
+
// 如果是空字符串,使用相对路径
|
37
|
+
if (url === '') {
|
38
|
+
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
39
|
+
return `${wsProtocol}//${window.location.host}/ws`;
|
40
|
+
}
|
41
|
+
// 将 http 转换为 ws,https 转换为 wss
|
42
|
+
const wsUrl = url.replace(/^http/, 'ws');
|
43
|
+
return wsUrl.endsWith('/ws') ? wsUrl : `${wsUrl}/ws`;
|
44
|
+
}
|
45
|
+
|
22
46
|
if (typeof window !== 'undefined') {
|
23
47
|
const hostname = window.location.hostname;
|
24
48
|
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
@@ -36,16 +60,37 @@ const getWsBaseUrl = () => {
|
|
36
60
|
const API_BASE_URL = getApiBaseUrl();
|
37
61
|
const WS_BASE_URL = getWsBaseUrl();
|
38
62
|
|
63
|
+
// 设置 axios 全局默认配置
|
64
|
+
// 空字符串时不设置 baseURL,让 axios 使用相对路径
|
65
|
+
if (API_BASE_URL !== '') {
|
66
|
+
axios.defaults.baseURL = API_BASE_URL;
|
67
|
+
}
|
68
|
+
|
39
69
|
// 创建 axios 实例
|
70
|
+
// 如果 baseURL 为空,设置一个请求拦截器来处理路径
|
40
71
|
const api = axios.create({
|
41
|
-
baseURL: API_BASE_URL,
|
72
|
+
baseURL: API_BASE_URL === '' ? undefined : API_BASE_URL, // 空字符串时不设置 baseURL
|
42
73
|
timeout: 10000,
|
43
74
|
});
|
44
75
|
|
76
|
+
// 如果是代理模式,确保路径正确
|
77
|
+
if (API_BASE_URL === '') {
|
78
|
+
api.interceptors.request.use(config => {
|
79
|
+
// 确保路径以 /api/ 开头
|
80
|
+
if (!config.url.startsWith('/api/')) {
|
81
|
+
config.url = '/api/' + config.url;
|
82
|
+
}
|
83
|
+
return config;
|
84
|
+
});
|
85
|
+
}
|
86
|
+
|
87
|
+
// 导出配置好的实例和URLs
|
88
|
+
export { api, API_BASE_URL, WS_BASE_URL };
|
89
|
+
|
45
90
|
// 获取全局统计信息
|
46
91
|
export const fetchGlobalStats = async () => {
|
47
92
|
try {
|
48
|
-
const response = await api.get('
|
93
|
+
const response = await api.get('stats'); // 不要前导斜杠,axios 会自动处理
|
49
94
|
return response.data.data;
|
50
95
|
} catch (error) {
|
51
96
|
console.error('Failed to fetch global stats:', error);
|
@@ -56,7 +101,7 @@ export const fetchGlobalStats = async () => {
|
|
56
101
|
// 获取队列列表
|
57
102
|
export const fetchQueues = async () => {
|
58
103
|
try {
|
59
|
-
const response = await api.get('
|
104
|
+
const response = await api.get('queues'); // 不要前导斜杠
|
60
105
|
return response.data;
|
61
106
|
} catch (error) {
|
62
107
|
console.error('Failed to fetch queues:', error);
|
@@ -69,7 +114,7 @@ export const fetchQueueTimeline = async (params) => {
|
|
69
114
|
try {
|
70
115
|
// 从参数中提取命名空间,如果没有则使用 default
|
71
116
|
const namespace = params.namespace || 'default';
|
72
|
-
const response = await api.post(
|
117
|
+
const response = await api.post(`queue-timeline/${namespace}`, params); // 不要前导斜杠
|
73
118
|
// 处理数据,确保不包含填充的空值
|
74
119
|
const data = response.data.data || response.data || [];
|
75
120
|
return {
|
@@ -3,6 +3,16 @@ import axios from 'axios';
|
|
3
3
|
// 在 React 应用中,环境变量需要以 REACT_APP_ 开头
|
4
4
|
// 使用 window.location 作为后备方案
|
5
5
|
const getApiBaseUrl = () => {
|
6
|
+
// 优先使用注入的 API URL
|
7
|
+
if (typeof window !== 'undefined' && window.JETTASK_API_URL !== undefined) {
|
8
|
+
const url = window.JETTASK_API_URL;
|
9
|
+
// 如果是空字符串,表示使用相对路径(代理模式)
|
10
|
+
if (url === '') {
|
11
|
+
return ''; // 使用相对路径
|
12
|
+
}
|
13
|
+
return url;
|
14
|
+
}
|
15
|
+
|
6
16
|
// 尝试从环境变量获取(Create React App 会在构建时注入)
|
7
17
|
if (typeof window !== 'undefined' && window.REACT_APP_API_URL) {
|
8
18
|
return window.REACT_APP_API_URL;
|
@@ -39,7 +49,9 @@ export const fetchQueueTrend = async (timeRange = '1h', queues = [], customTimeR
|
|
39
49
|
params.end = customTimeRange[1].valueOf();
|
40
50
|
}
|
41
51
|
|
42
|
-
|
52
|
+
// 当 API_BASE_URL 为空时,使用相对路径;否则需要加 /api
|
53
|
+
const url = API_BASE_URL === '' ? '/api/queue-trend' : `${API_BASE_URL}/api/queue-trend`;
|
54
|
+
const response = await axios.get(url, { params });
|
43
55
|
|
44
56
|
// 处理响应数据 - 后端现在直接返回时间线数据
|
45
57
|
const data = response.data.data || response.data || [];
|
@@ -132,7 +144,9 @@ const getIntervalByRange = (range) => {
|
|
132
144
|
*/
|
133
145
|
export const fetchQueueStats = async (queueName, timeRange = '1h') => {
|
134
146
|
try {
|
135
|
-
|
147
|
+
// 当 API_BASE_URL 为空时,使用相对路径;否则需要加 /api
|
148
|
+
const url = API_BASE_URL === '' ? `/api/queue/${queueName}/stats` : `${API_BASE_URL}/api/queue/${queueName}/stats`;
|
149
|
+
const response = await axios.get(url, {
|
136
150
|
params: { range: timeRange }
|
137
151
|
});
|
138
152
|
|
@@ -7,7 +7,7 @@ jettask/config/performance.py,sha256=bOdLEskfB_6cRfS10IRgmtKEsJw_CaIZsPHbXxaHwbU
|
|
7
7
|
jettask/core/__init__.py,sha256=CvBoBCERXCo-jgnkPqAuIgT4uC7oQMnSi7okRxMi6Vc,181
|
8
8
|
jettask/core/app.py,sha256=D6wqdGIVR8E1a7RNevNDaICfKGDsea4RqS9aRlMU810,74906
|
9
9
|
jettask/core/app_importer.py,sha256=B8WiSUz5_O5jlFIBr1CxI_F2gqFYK6ItpONiY_4AiXI,10266
|
10
|
-
jettask/core/cli.py,sha256=
|
10
|
+
jettask/core/cli.py,sha256=HagyhlJcp_KspuzJ6NhAmweD8N5qzh8ks-s0F0t91uA,31580
|
11
11
|
jettask/core/consumer_manager.py,sha256=7z3IBvH85YD61ZkVNfOVsuP5NaAwV2Ki8aVz9TCplAM,79870
|
12
12
|
jettask/core/context.py,sha256=XI4Q1s75NIWImcrHIkCLgGo_kpuJIk8MlBoNIJuEfF0,993
|
13
13
|
jettask/core/delay_scanner.py,sha256=rwbIA7SFyOxAV14FW7NB40_djFrrqJJpNkpOri7XcZI,8394
|
@@ -92,7 +92,7 @@ jettask/webui/backend/main_unified.py,sha256=4owlt7n1Axg2zwzmaACHkuIZTj-KJonXxjK
|
|
92
92
|
jettask/webui/backend/main_v2.py,sha256=XU7LZriOuTcMHt8rZTeNUAtY7HBIu50vzJT-f8_AbCs,11603
|
93
93
|
jettask/webui/backend/namespace_api.py,sha256=Q-VmwszGjJXrGI_GlNM356l1TSprRENE-p1vtR-WUt0,11310
|
94
94
|
jettask/webui/backend/namespace_api_old.py,sha256=lGmkWurtt5LTsTDzjkG8q3rhyMrzzm73VUFkCmafI5Y,11240
|
95
|
-
jettask/webui/backend/namespace_data_access.py,sha256=
|
95
|
+
jettask/webui/backend/namespace_data_access.py,sha256=Cw2_FLm5MqRKEVLaRyM6qBpIU8Azb0qPIeXBTfNeRWM,25823
|
96
96
|
jettask/webui/backend/queue_backlog_api.py,sha256=HQphpAj4gFRxw7S4h1LC3HxM6eWWMleNr7cDSaxKnyI,26681
|
97
97
|
jettask/webui/backend/queue_stats_v2.py,sha256=VeI46WQREFAQgqW330eKs0jO7ZBehLIEKsxAAu907T8,24402
|
98
98
|
jettask/webui/backend/redis_monitor_api.py,sha256=fmDCzHAm2jtx1IhKZ299W5IS2CSI1RS7EC9V3rDtELs,18287
|
@@ -118,7 +118,7 @@ jettask/webui/frontend/vite.config.js,sha256=WqgVesX8iqlLFtWq7u4L33hdJXaDqy9EOJJ
|
|
118
118
|
jettask/webui/frontend/src/App.css,sha256=TLwSOdKNsUtfAtnTyCxeLZBkRhhw34zWeA8zhEP7dDs,1753
|
119
119
|
jettask/webui/frontend/src/App.jsx,sha256=wyTfHb3hp0dThO9fhn4r8JLc9RIkaxnwOieY0o8e7tg,2497
|
120
120
|
jettask/webui/frontend/src/index.css,sha256=s2mxkzEDoY10XqRcV_5_32r4GZk22cvZoBMh6_NKVq0,2074
|
121
|
-
jettask/webui/frontend/src/main.jsx,sha256=
|
121
|
+
jettask/webui/frontend/src/main.jsx,sha256=GCb7lGMl--XLnsJz2PQX2MoXHR5hMS-mcbojnkyvGYY,582
|
122
122
|
jettask/webui/frontend/src/components/NamespaceSelector.jsx,sha256=xUIvLPhY_KdUQEHJD7sxim5ZCprk6lK2RRyI3qIs9UQ,5341
|
123
123
|
jettask/webui/frontend/src/components/QueueBacklogChart.jsx,sha256=fzwI4QBUN8lBuE89Sf-7fM3kFgH5J8mbDCPyHjkL8ao,8114
|
124
124
|
jettask/webui/frontend/src/components/QueueBacklogTrend.jsx,sha256=SPwEeGWDoxemeIrcRqgEA0Yx59VUO4cNvOx_EHCIp_I,21255
|
@@ -151,22 +151,22 @@ jettask/webui/frontend/src/pages/Dashboard.jsx,sha256=uMD5hIpgFgapACkz_exQI-tz5u
|
|
151
151
|
jettask/webui/frontend/src/pages/QueueDetail.jsx,sha256=04LlmiIf5qJiPh6ilTIBb1x9mAQHkza7vA2QeM75Nqo,36054
|
152
152
|
jettask/webui/frontend/src/pages/QueueMonitor.jsx,sha256=zp5eR0jBiqZktzsP6NsbTbIo7J7Vr2pwdyozMakB1gE,17749
|
153
153
|
jettask/webui/frontend/src/pages/Queues.jsx,sha256=FRE5EMaW3ln_yU9K1_LyFDLgfwmR7jUqIcC_n6mVPVk,201
|
154
|
-
jettask/webui/frontend/src/pages/ScheduledTasks.jsx,sha256=
|
155
|
-
jettask/webui/frontend/src/pages/Settings.jsx,sha256=
|
154
|
+
jettask/webui/frontend/src/pages/ScheduledTasks.jsx,sha256=QvFVE2gTMxhtkso8pjXlNT1apwW3KR6aTz-FHs8lZRs,24034
|
155
|
+
jettask/webui/frontend/src/pages/Settings.jsx,sha256=Zn-YiOlEWKHrJTqR4PIYoyJ_HSycVFeiMUfpp9vpFmc,26570
|
156
156
|
jettask/webui/frontend/src/pages/Workers.jsx,sha256=S3JI-gDfkBuAIeHQdBCyFg8OuwOa970Kz4cgBGqaAmo,227
|
157
157
|
jettask/webui/frontend/src/pages/Dashboard/index.css,sha256=DNzBEusK0LtDgcVie_Tg-bcKHs_0EtLUPtqnBfYdERs,511
|
158
158
|
jettask/webui/frontend/src/pages/Dashboard/index.jsx,sha256=h-852VjLYcuoDGlkmJ0odrnLU554NsFh332lK99AzWI,7873
|
159
|
-
jettask/webui/frontend/src/services/api.js,sha256=
|
160
|
-
jettask/webui/frontend/src/services/queueTrend.js,sha256=
|
159
|
+
jettask/webui/frontend/src/services/api.js,sha256=Oui7OeiRCCjHXR6ULeVDovpDPEySdUmK2j6qjxJjtMk,4641
|
160
|
+
jettask/webui/frontend/src/services/queueTrend.js,sha256=ljFAxuO2-JTqM_C0zZuF6Wa8edUCu4QdtsYT6bv0dLQ,5207
|
161
161
|
jettask/webui/frontend/src/utils/suppressWarnings.js,sha256=L2_mnKbkh7_5l0JkLDOxXe6smgMsv7I3sB0ANGrS2JA,678
|
162
162
|
jettask/webui/frontend/src/utils/userPreferences.js,sha256=u9VC47XrG0fl0tfx4dY5BBs4ztmGQR0oS-V8LYVw8a4,4685
|
163
163
|
jettask/webui/models/__init__.py,sha256=5cv0oZksj1B3_rzCqsPmF3Gn9NRZLwzMnaJ8bOKGB4I,25
|
164
164
|
jettask/webui/models/namespace.py,sha256=jDj-hZF_-wXzrWAWVDyZVU0JUWDax9apb4Gyykwg-rE,2006
|
165
165
|
jettask/webui/sql/batch_upsert_functions.sql,sha256=5eWOhOD8gWHhtcop_BrCpZTxPFeyBHtt_leNQZO89Cs,6615
|
166
166
|
jettask/webui/sql/init_database.sql,sha256=CSjhBZldtfV0SGBLTFf576ALaRfCFe3wywpezzkX1TQ,22369
|
167
|
-
jettask-0.2.
|
168
|
-
jettask-0.2.
|
169
|
-
jettask-0.2.
|
170
|
-
jettask-0.2.
|
171
|
-
jettask-0.2.
|
172
|
-
jettask-0.2.
|
167
|
+
jettask-0.2.14.dist-info/licenses/LICENSE,sha256=sKR8OPWvnqxzcHAmnaVSANpRpeM0Z52PNLCB0ZlFN6c,1062
|
168
|
+
jettask-0.2.14.dist-info/METADATA,sha256=FOi8o9_uBTUDLricQRla6hTHpCaG6vigrp6GZiSch5Q,2956
|
169
|
+
jettask-0.2.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
170
|
+
jettask-0.2.14.dist-info/entry_points.txt,sha256=VC3byRkkSfRHu_QzczGtpGjcFERkJUlCrD__TFLVqxI,153
|
171
|
+
jettask-0.2.14.dist-info/top_level.txt,sha256=uymyRUF87-OsSurk5NhpeTW0jy3Wltnn91Zoa6jmAaw,8
|
172
|
+
jettask-0.2.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|