jpsclient 2.2.0 → 2.3.0
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.
- checksums.yaml +4 -4
- data/lib/jpsclient/api/android_jks.rb +7 -70
- data/lib/jpsclient/api/app_level.rb +6 -60
- data/lib/jpsclient/api/app_resource.rb +7 -70
- data/lib/jpsclient/api/apple_account.rb +6 -60
- data/lib/jpsclient/api/apple_bundle_id.rb +1 -11
- data/lib/jpsclient/api/apple_cert.rb +1 -11
- data/lib/jpsclient/api/apple_profile.rb +1 -11
- data/lib/jpsclient/api/application.rb +12 -121
- data/lib/jpsclient/api/application_income.rb +2 -20
- data/lib/jpsclient/api/application_sales.rb +2 -20
- data/lib/jpsclient/api/application_version.rb +3 -30
- data/lib/jpsclient/api/archived_outdated/app_resource_version.rb +2 -20
- data/lib/jpsclient/api/archived_outdated/application_category.rb +5 -50
- data/lib/jpsclient/api/archived_outdated/application_design.rb +5 -50
- data/lib/jpsclient/api/archived_outdated/assets_category.rb +5 -50
- data/lib/jpsclient/api/archived_outdated/experience.rb +6 -60
- data/lib/jpsclient/api/archived_outdated/experience_category.rb +4 -40
- data/lib/jpsclient/api/archived_outdated/icon_and_snapshot.rb +2 -20
- data/lib/jpsclient/api/archived_outdated/publisher_category.rb +6 -60
- data/lib/jpsclient/api/archived_outdated/publisher_group_category.rb +5 -50
- data/lib/jpsclient/api/archived_outdated/requirements_category.rb +4 -40
- data/lib/jpsclient/api/archived_outdated/resource_category.rb +5 -50
- data/lib/jpsclient/api/archived_outdated/sketch_category.rb +4 -40
- data/lib/jpsclient/api/archived_outdated/survey_category.rb +4 -40
- data/lib/jpsclient/api/archived_outdated/tool_category.rb +5 -50
- data/lib/jpsclient/api/bug.rb +6 -60
- data/lib/jpsclient/api/category.rb +6 -60
- data/lib/jpsclient/api/cert.rb +1 -12
- data/lib/jpsclient/api/collect.rb +2 -20
- data/lib/jpsclient/api/collection.rb +7 -70
- data/lib/jpsclient/api/commit_log.rb +5 -52
- data/lib/jpsclient/api/creative.rb +2 -20
- data/lib/jpsclient/api/custom_application.rb +10 -100
- data/lib/jpsclient/api/custom_application_web.rb +2 -20
- data/lib/jpsclient/api/design.rb +5 -50
- data/lib/jpsclient/api/document_text.rb +1 -10
- data/lib/jpsclient/api/fgui_export.rb +2 -20
- data/lib/jpsclient/api/file.rb +4 -41
- data/lib/jpsclient/api/game_assets.rb +6 -60
- data/lib/jpsclient/api/healthy.rb +1 -10
- data/lib/jpsclient/api/idea.rb +5 -50
- data/lib/jpsclient/api/image_search.rb +2 -20
- data/lib/jpsclient/api/js_sdk.rb +1 -10
- data/lib/jpsclient/api/lark_bitable.rb +1 -10
- data/lib/jpsclient/api/lark_card_message.rb +1 -10
- data/lib/jpsclient/api/lark_chat_group.rb +1 -10
- data/lib/jpsclient/api/lark_comment.rb +5 -50
- data/lib/jpsclient/api/lark_department.rb +1 -10
- data/lib/jpsclient/api/lark_file.rb +1 -10
- data/lib/jpsclient/api/lark_leave_approval.rb +1 -10
- data/lib/jpsclient/api/lark_message.rb +1 -10
- data/lib/jpsclient/api/lark_task.rb +6 -60
- data/lib/jpsclient/api/lark_task_list.rb +5 -50
- data/lib/jpsclient/api/lark_task_section.rb +3 -30
- data/lib/jpsclient/api/lark_user.rb +1 -10
- data/lib/jpsclient/api/lark_wiki_node.rb +1 -10
- data/lib/jpsclient/api/lark_wiki_space.rb +1 -10
- data/lib/jpsclient/api/lazy_client.rb +39 -39
- data/lib/jpsclient/api/login.rb +4 -40
- data/lib/jpsclient/api/m3u8.rb +1 -10
- data/lib/jpsclient/api/menu.rb +6 -60
- data/lib/jpsclient/api/modular_client.rb +38 -38
- data/lib/jpsclient/api/nuget.rb +1 -10
- data/lib/jpsclient/api/permission.rb +5 -50
- data/lib/jpsclient/api/project.rb +2 -22
- data/lib/jpsclient/api/project_package.rb +9 -121
- data/lib/jpsclient/api/publisher.rb +7 -70
- data/lib/jpsclient/api/publisher_group.rb +5 -50
- data/lib/jpsclient/api/requirements.rb +6 -60
- data/lib/jpsclient/api/role.rb +7 -70
- data/lib/jpsclient/api/simple_search.rb +7 -70
- data/lib/jpsclient/api/sketch.rb +3 -30
- data/lib/jpsclient/api/sov.rb +1 -10
- data/lib/jpsclient/api/statistics.rb +1 -10
- data/lib/jpsclient/api/store.rb +1 -10
- data/lib/jpsclient/api/survey.rb +6 -60
- data/lib/jpsclient/api/tag.rb +6 -60
- data/lib/jpsclient/api/template.rb +5 -50
- data/lib/jpsclient/api/tool.rb +5 -50
- data/lib/jpsclient/api/trending.rb +1 -10
- data/lib/jpsclient/api/ud_id.rb +5 -50
- data/lib/jpsclient/api/user.rb +4 -40
- data/lib/jpsclient/api/util.rb +1 -10
- data/lib/jpsclient/api/video_cover.rb +1 -10
- data/lib/jpsclient/api/webhook.rb +5 -50
- data/lib/jpsclient/api/workflow.rb +5 -50
- data/lib/jpsclient/auth/auth.rb +21 -31
- data/lib/jpsclient/auth/token.rb +49 -95
- data/lib/jpsclient/base/client.rb +43 -38
- data/lib/jpsclient/http/http_client.rb +18 -2
- data/lib/jpsclient/version.rb +1 -1
- metadata +15 -1
|
@@ -14,16 +14,7 @@ module JPSClient
|
|
|
14
14
|
|
|
15
15
|
path = config["url"]
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
result = JPSClient::Response.new(response)
|
|
19
|
-
|
|
20
|
-
if result.need_login?
|
|
21
|
-
do_login(force_login: true)
|
|
22
|
-
response = @http_client.post(path, body: params)
|
|
23
|
-
result = JPSClient::Response.new(response)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
return result.to_h
|
|
17
|
+
return request_with_auth(:post, path, body: params)
|
|
27
18
|
end
|
|
28
19
|
|
|
29
20
|
# Create Sort
|
|
@@ -36,16 +27,7 @@ module JPSClient
|
|
|
36
27
|
|
|
37
28
|
path = config["url"]
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
result = JPSClient::Response.new(response)
|
|
41
|
-
|
|
42
|
-
if result.need_login?
|
|
43
|
-
do_login(force_login: true)
|
|
44
|
-
response = @http_client.post(path, body: params)
|
|
45
|
-
result = JPSClient::Response.new(response)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
return result.to_h
|
|
30
|
+
return request_with_auth(:post, path, body: params)
|
|
49
31
|
end
|
|
50
32
|
|
|
51
33
|
# Create Delete
|
|
@@ -58,16 +40,7 @@ module JPSClient
|
|
|
58
40
|
|
|
59
41
|
path = config["url"]
|
|
60
42
|
|
|
61
|
-
|
|
62
|
-
result = JPSClient::Response.new(response)
|
|
63
|
-
|
|
64
|
-
if result.need_login?
|
|
65
|
-
do_login(force_login: true)
|
|
66
|
-
response = @http_client.post(path, body: params)
|
|
67
|
-
result = JPSClient::Response.new(response)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
return result.to_h
|
|
43
|
+
return request_with_auth(:post, path, body: params)
|
|
71
44
|
end
|
|
72
45
|
|
|
73
46
|
# Create Create
|
|
@@ -80,16 +53,7 @@ module JPSClient
|
|
|
80
53
|
|
|
81
54
|
path = config["url"]
|
|
82
55
|
|
|
83
|
-
|
|
84
|
-
result = JPSClient::Response.new(response)
|
|
85
|
-
|
|
86
|
-
if result.need_login?
|
|
87
|
-
do_login(force_login: true)
|
|
88
|
-
response = @http_client.post(path, body: params)
|
|
89
|
-
result = JPSClient::Response.new(response)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
return result.to_h
|
|
56
|
+
return request_with_auth(:post, path, body: params)
|
|
93
57
|
end
|
|
94
58
|
|
|
95
59
|
# Get List
|
|
@@ -102,16 +66,7 @@ module JPSClient
|
|
|
102
66
|
|
|
103
67
|
path = config["url"]
|
|
104
68
|
|
|
105
|
-
|
|
106
|
-
result = JPSClient::Response.new(response)
|
|
107
|
-
|
|
108
|
-
if result.need_login?
|
|
109
|
-
do_login(force_login: true)
|
|
110
|
-
response = @http_client.get(path, params: params)
|
|
111
|
-
result = JPSClient::Response.new(response)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
return result.to_h
|
|
69
|
+
return request_with_auth(:get, path, params: params)
|
|
115
70
|
end
|
|
116
71
|
|
|
117
72
|
# 获取指定项目的工作流列表
|
data/lib/jpsclient/auth/auth.rb
CHANGED
|
@@ -18,8 +18,8 @@ module JPSClient
|
|
|
18
18
|
# JPS 登录配置类
|
|
19
19
|
class LoginConfig
|
|
20
20
|
attr_accessor :client_id, :feishu_auth_url, :redirect_uri, :api_endpoint, :state, :server_port
|
|
21
|
-
attr_accessor :aes_key
|
|
22
|
-
attr_accessor :token_dir, :token_file_name
|
|
21
|
+
attr_accessor :aes_key
|
|
22
|
+
attr_accessor :token_dir, :token_file_name
|
|
23
23
|
|
|
24
24
|
def initialize
|
|
25
25
|
# 所有配置必须从外部配置文件加载,不提供硬编码的默认值
|
|
@@ -30,7 +30,6 @@ module JPSClient
|
|
|
30
30
|
@state = 'client_login' # 仅此项可有默认值
|
|
31
31
|
@server_port = 8898 # 仅此项可有默认值
|
|
32
32
|
@aes_key = nil # AES 加密密钥,必需配置
|
|
33
|
-
@token_verify_endpoint = nil # Token 验证端点,必需配置
|
|
34
33
|
@token_dir = nil # Token 存储目录,可选配置
|
|
35
34
|
@token_file_name = nil # Token 文件名,可选配置
|
|
36
35
|
end
|
|
@@ -49,7 +48,6 @@ module JPSClient
|
|
|
49
48
|
config.api_endpoint = data['api_endpoint'] if data['api_endpoint']
|
|
50
49
|
config.state = data['state'] if data['state']
|
|
51
50
|
config.server_port = data['server_port'] if data['server_port']
|
|
52
|
-
config.token_verify_endpoint = data['token_verify_endpoint'] if data['token_verify_endpoint']
|
|
53
51
|
config.aes_key = data['aes_key'] if data['aes_key']
|
|
54
52
|
config.token_dir = data['token_dir'] if data['token_dir'] # 加载 token 目录配置
|
|
55
53
|
config.token_file_name = data['token_file_name'] if data['token_file_name'] # 加载 token 文件名配置
|
|
@@ -65,7 +63,8 @@ module JPSClient
|
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
class Auth
|
|
68
|
-
attr_reader :access_token, :username
|
|
66
|
+
attr_reader :access_token, :username
|
|
67
|
+
attr_reader :user_id, :permissions, :lark_user_id, :tenant_manager
|
|
69
68
|
|
|
70
69
|
def initialize(config = nil)
|
|
71
70
|
# 只接受 LoginConfig 对象或默认配置
|
|
@@ -103,7 +102,10 @@ module JPSClient
|
|
|
103
102
|
|
|
104
103
|
@access_token = nil
|
|
105
104
|
@username = nil
|
|
106
|
-
@
|
|
105
|
+
@user_id = nil
|
|
106
|
+
@permissions = nil
|
|
107
|
+
@lark_user_id = nil
|
|
108
|
+
@tenant_manager = false
|
|
107
109
|
|
|
108
110
|
# 调试模式,通过环境变量控制
|
|
109
111
|
@verbose = ENV['PINDO_DEBUG'] == 'true'
|
|
@@ -137,7 +139,10 @@ module JPSClient
|
|
|
137
139
|
{
|
|
138
140
|
'token' => @access_token,
|
|
139
141
|
'username' => @username,
|
|
140
|
-
'
|
|
142
|
+
'user_id' => @user_id,
|
|
143
|
+
'permissions' => @permissions,
|
|
144
|
+
'lark_user_id' => @lark_user_id,
|
|
145
|
+
'tenant_manager' => @tenant_manager
|
|
141
146
|
}
|
|
142
147
|
end
|
|
143
148
|
|
|
@@ -251,26 +256,6 @@ module JPSClient
|
|
|
251
256
|
end
|
|
252
257
|
end
|
|
253
258
|
|
|
254
|
-
# 检查 token 是否有效
|
|
255
|
-
def validate_token(token = nil)
|
|
256
|
-
token_to_check = token || @access_token
|
|
257
|
-
return false unless token_to_check
|
|
258
|
-
|
|
259
|
-
# 检查过期时间
|
|
260
|
-
if @expires_at && Time.now.to_i > @expires_at
|
|
261
|
-
puts "Token 已过期,需要重新登录"
|
|
262
|
-
return false
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
# 简单验证(实际项目中可以调用 API 验证)
|
|
266
|
-
return true
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
# 验证Pgyer令牌(保留兼容性)
|
|
270
|
-
def validate_pgyer_token(token = nil, expires_at = nil)
|
|
271
|
-
validate_token(token)
|
|
272
|
-
end
|
|
273
|
-
|
|
274
259
|
# 使用授权码换取 token
|
|
275
260
|
def exchange_code_for_token(code)
|
|
276
261
|
begin
|
|
@@ -312,10 +297,13 @@ module JPSClient
|
|
|
312
297
|
|
|
313
298
|
if result['meta'] && result['meta']['code'] == 0 && result['data']
|
|
314
299
|
data = result['data']
|
|
300
|
+
# 兼容 snake_case 和 camelCase 两种响应格式
|
|
315
301
|
@access_token = data['token'] if data['token']
|
|
316
302
|
@username = data['username'] if data['username']
|
|
317
|
-
|
|
318
|
-
@
|
|
303
|
+
@user_id = data['user_id'] || data['userId']
|
|
304
|
+
@permissions = data['permissions']
|
|
305
|
+
@lark_user_id = data['lark_user_id'] || data['larkUserId']
|
|
306
|
+
@tenant_manager = data['tenant_manager'] || data['tenantManager'] || false
|
|
319
307
|
|
|
320
308
|
return true if @access_token
|
|
321
309
|
else
|
|
@@ -452,8 +440,8 @@ module JPSClient
|
|
|
452
440
|
end
|
|
453
441
|
end
|
|
454
442
|
|
|
455
|
-
# 捕获 Ctrl+C
|
|
456
|
-
trap('INT') { server.shutdown }
|
|
443
|
+
# 捕获 Ctrl+C,结束后恢复默认行为
|
|
444
|
+
previous_trap = trap('INT') { server.shutdown }
|
|
457
445
|
|
|
458
446
|
# 在线程中运行服务器,最多等待 3 分钟
|
|
459
447
|
thread = Thread.new { server.start }
|
|
@@ -462,6 +450,8 @@ module JPSClient
|
|
|
462
450
|
rescue => e
|
|
463
451
|
puts "服务器等待超时"
|
|
464
452
|
server.shutdown
|
|
453
|
+
ensure
|
|
454
|
+
trap('INT', previous_trap || 'DEFAULT')
|
|
465
455
|
end
|
|
466
456
|
|
|
467
457
|
code
|
data/lib/jpsclient/auth/token.rb
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
4
|
require 'fileutils'
|
|
5
|
-
require 'net/http'
|
|
6
|
-
require 'uri'
|
|
7
5
|
require 'jpsclient/utils/aes'
|
|
8
|
-
require 'jpsclient/base/exception'
|
|
9
6
|
require 'jpsclient/utils/logger'
|
|
10
7
|
|
|
11
8
|
module JPSClient
|
|
12
9
|
|
|
13
10
|
# Token 管理类
|
|
11
|
+
# 负责 token 的本地存储、加载和清除
|
|
12
|
+
# token 有效性由服务端 401 响应判断,本地不做过期检查
|
|
14
13
|
class Token
|
|
15
|
-
attr_reader :token, :username
|
|
14
|
+
attr_reader :token, :username
|
|
15
|
+
attr_reader :user_id, :permissions, :lark_user_id, :tenant_manager
|
|
16
16
|
|
|
17
17
|
def initialize(config)
|
|
18
18
|
@config = config
|
|
@@ -35,8 +35,10 @@ module JPSClient
|
|
|
35
35
|
# token 数据
|
|
36
36
|
@token = nil
|
|
37
37
|
@username = nil
|
|
38
|
-
@
|
|
39
|
-
@
|
|
38
|
+
@user_id = nil
|
|
39
|
+
@permissions = nil
|
|
40
|
+
@lark_user_id = nil
|
|
41
|
+
@tenant_manager = false
|
|
40
42
|
|
|
41
43
|
# 调试模式
|
|
42
44
|
@verbose = ENV['PINDO_DEBUG'] == 'true'
|
|
@@ -66,10 +68,19 @@ module JPSClient
|
|
|
66
68
|
|
|
67
69
|
@token = token_data['token']
|
|
68
70
|
@username = token_data['username']
|
|
69
|
-
@
|
|
70
|
-
@
|
|
71
|
+
@user_id = token_data['user_id']
|
|
72
|
+
@permissions = token_data['permissions']
|
|
73
|
+
@lark_user_id = token_data['lark_user_id']
|
|
74
|
+
@tenant_manager = token_data.key?('tenant_manager') ? token_data['tenant_manager'] : false
|
|
75
|
+
|
|
76
|
+
# 旧版 token 文件缺少 user_id,视为无效,需重新登录获取完整字段
|
|
77
|
+
unless @token && @user_id
|
|
78
|
+
puts "Token 文件缺少必要字段,需要重新登录" if @verbose
|
|
79
|
+
clear
|
|
80
|
+
return false
|
|
81
|
+
end
|
|
71
82
|
|
|
72
|
-
return true
|
|
83
|
+
return true
|
|
73
84
|
rescue => e
|
|
74
85
|
puts "读取 token 失败: #{e.message}" if @verbose
|
|
75
86
|
clear_corrupted_file
|
|
@@ -79,30 +90,35 @@ module JPSClient
|
|
|
79
90
|
end
|
|
80
91
|
|
|
81
92
|
# 保存 token
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
# 传入完整数据 Hash
|
|
94
|
+
def save(token_data)
|
|
95
|
+
return false unless token_data.is_a?(Hash) && token_data['token']
|
|
84
96
|
|
|
85
|
-
@token = token
|
|
86
|
-
@username = username
|
|
87
|
-
@
|
|
88
|
-
@
|
|
97
|
+
@token = token_data['token']
|
|
98
|
+
@username = token_data['username']
|
|
99
|
+
@user_id = token_data['user_id']
|
|
100
|
+
@permissions = token_data['permissions']
|
|
101
|
+
@lark_user_id = token_data['lark_user_id']
|
|
102
|
+
@tenant_manager = token_data.key?('tenant_manager') ? token_data['tenant_manager'] : false
|
|
89
103
|
|
|
90
104
|
# 确保目录存在
|
|
91
105
|
FileUtils.mkdir_p(@token_dir) unless Dir.exist?(@token_dir)
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
save_data = {
|
|
94
108
|
'token' => @token,
|
|
95
109
|
'username' => @username,
|
|
96
|
-
'
|
|
97
|
-
'
|
|
110
|
+
'user_id' => @user_id,
|
|
111
|
+
'permissions' => @permissions,
|
|
112
|
+
'lark_user_id' => @lark_user_id,
|
|
113
|
+
'tenant_manager' => @tenant_manager
|
|
98
114
|
}
|
|
99
115
|
|
|
100
116
|
# 根据是否有 AES 密钥决定加密方式
|
|
101
117
|
content = if @aes_key
|
|
102
118
|
aes = AES.new(@aes_key)
|
|
103
|
-
aes.encrypt(
|
|
119
|
+
aes.encrypt(save_data.to_json)
|
|
104
120
|
else
|
|
105
|
-
|
|
121
|
+
save_data.to_json
|
|
106
122
|
end
|
|
107
123
|
|
|
108
124
|
File.write(@token_file, content)
|
|
@@ -114,106 +130,44 @@ module JPSClient
|
|
|
114
130
|
false
|
|
115
131
|
end
|
|
116
132
|
|
|
117
|
-
#
|
|
118
|
-
def
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# 1. 检查本地时间过期
|
|
122
|
-
if expired?
|
|
123
|
-
puts "Token 已过期 (本地时间检查)" if @verbose
|
|
124
|
-
return false
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# 2. 可选:API 验证
|
|
128
|
-
# 为了避免频繁调用,只在接近过期时验证
|
|
129
|
-
if should_verify_with_api?
|
|
130
|
-
return verify_with_api
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
true
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# 检查是否过期
|
|
137
|
-
def expired?
|
|
138
|
-
return true unless @expires_at
|
|
139
|
-
Time.now.to_i > @expires_at
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# API 验证 token
|
|
143
|
-
def verify_with_api
|
|
144
|
-
return false unless @token && @config
|
|
145
|
-
|
|
146
|
-
begin
|
|
147
|
-
# 使用配置中的验证端点
|
|
148
|
-
base_url = @config.api_endpoint.split('/api/')[0] # 获取基础 URL
|
|
149
|
-
verify_endpoint = @config.respond_to?(:token_verify_endpoint) ? @config.token_verify_endpoint : '/api/user/profile'
|
|
150
|
-
uri = URI("#{base_url}#{verify_endpoint}")
|
|
151
|
-
|
|
152
|
-
request = Net::HTTP::Get.new(uri)
|
|
153
|
-
request['Authorization'] = "Bearer #{@token}"
|
|
154
|
-
|
|
155
|
-
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
156
|
-
http.request(request)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
if response.code == '200'
|
|
160
|
-
puts "Token API 验证成功" if @verbose
|
|
161
|
-
return true
|
|
162
|
-
else
|
|
163
|
-
puts "Token API 验证失败: #{response.code}" if @verbose
|
|
164
|
-
return false
|
|
165
|
-
end
|
|
166
|
-
rescue => e
|
|
167
|
-
puts "Token API 验证出错: #{e.message}" if @verbose
|
|
168
|
-
# API 验证失败时,回退到本地验证
|
|
169
|
-
return !expired?
|
|
170
|
-
end
|
|
133
|
+
# token 是否已加载
|
|
134
|
+
def loaded?
|
|
135
|
+
!@token.nil? && !@token.empty?
|
|
171
136
|
end
|
|
172
137
|
|
|
173
138
|
# 清除 token
|
|
174
139
|
def clear
|
|
175
140
|
@token = nil
|
|
176
141
|
@username = nil
|
|
177
|
-
@
|
|
178
|
-
@
|
|
142
|
+
@user_id = nil
|
|
143
|
+
@permissions = nil
|
|
144
|
+
@lark_user_id = nil
|
|
145
|
+
@tenant_manager = false
|
|
179
146
|
|
|
180
147
|
FileUtils.rm_f(@token_file) if File.exist?(@token_file)
|
|
181
148
|
puts "✓ Token 已清除" if @verbose
|
|
182
149
|
end
|
|
183
150
|
|
|
184
|
-
# 转换为 Hash
|
|
151
|
+
# 转换为 Hash
|
|
185
152
|
def to_h
|
|
186
153
|
return nil unless @token
|
|
187
154
|
|
|
188
155
|
{
|
|
189
156
|
'token' => @token,
|
|
190
157
|
'username' => @username,
|
|
191
|
-
'
|
|
158
|
+
'user_id' => @user_id,
|
|
159
|
+
'permissions' => @permissions,
|
|
160
|
+
'lark_user_id' => @lark_user_id,
|
|
161
|
+
'tenant_manager' => @tenant_manager
|
|
192
162
|
}
|
|
193
163
|
end
|
|
194
164
|
|
|
195
|
-
# 从 Auth 实例更新 token
|
|
196
|
-
def update_from_auth(auth)
|
|
197
|
-
return false unless auth.access_token
|
|
198
|
-
|
|
199
|
-
save(auth.access_token, auth.username, auth.expires_at)
|
|
200
|
-
end
|
|
201
|
-
|
|
202
165
|
private
|
|
203
166
|
|
|
204
|
-
# 是否应该通过 API 验证
|
|
205
|
-
def should_verify_with_api?
|
|
206
|
-
return false unless @expires_at
|
|
207
|
-
|
|
208
|
-
# 策略:最后 24 小时内进行 API 验证
|
|
209
|
-
remaining_time = @expires_at - Time.now.to_i
|
|
210
|
-
remaining_time > 0 && remaining_time < 24 * 60 * 60
|
|
211
|
-
end
|
|
212
|
-
|
|
213
167
|
# 清除损坏的文件
|
|
214
168
|
def clear_corrupted_file
|
|
215
169
|
FileUtils.rm_f(@token_file) if File.exist?(@token_file)
|
|
216
170
|
puts "已清除损坏的 token 文件" if @verbose
|
|
217
171
|
end
|
|
218
172
|
end
|
|
219
|
-
end
|
|
173
|
+
end
|
|
@@ -226,7 +226,7 @@ module JPSClient
|
|
|
226
226
|
@token_manager = Token.new(@jps_config)
|
|
227
227
|
|
|
228
228
|
# 尝试加载已保存的 token
|
|
229
|
-
if @token_manager.load && @token_manager.
|
|
229
|
+
if @token_manager.load && @token_manager.loaded?
|
|
230
230
|
@token = @token_manager.to_h
|
|
231
231
|
end
|
|
232
232
|
|
|
@@ -258,50 +258,55 @@ module JPSClient
|
|
|
258
258
|
end
|
|
259
259
|
|
|
260
260
|
# 核心登录功能
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
# token 有效性由服务端 401 响应判断,本地不做过期检查
|
|
262
|
+
def do_login(force_login: false)
|
|
263
263
|
if force_login
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if result == :user_cancelled
|
|
270
|
-
Logger.instance.fancyinfo_error("用户取消了登录操作")
|
|
271
|
-
return false
|
|
272
|
-
elsif result == true
|
|
273
|
-
# 保存新 token
|
|
274
|
-
@token_manager.save(auth.access_token, auth.username, auth.expires_at)
|
|
275
|
-
update_token_everywhere()
|
|
264
|
+
@token_manager.clear
|
|
265
|
+
else
|
|
266
|
+
# 本地存在 token,直接使用
|
|
267
|
+
if @token_manager.load && @token_manager.loaded?
|
|
268
|
+
update_token_everywhere
|
|
276
269
|
return true
|
|
277
|
-
else
|
|
278
|
-
Logger.instance.fancyinfo_error("登录失败,未能获取有效token")
|
|
279
|
-
return false
|
|
280
270
|
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# 需要重新登录
|
|
274
|
+
auth = Auth.new(@jps_config)
|
|
275
|
+
result = auth.login
|
|
276
|
+
|
|
277
|
+
if result == :user_cancelled
|
|
278
|
+
Logger.instance.fancyinfo_error("用户取消了登录操作")
|
|
279
|
+
return false
|
|
280
|
+
elsif result == true
|
|
281
|
+
unless @token_manager.save(auth.get_token_data)
|
|
282
|
+
Logger.instance.fancyinfo_error("Token 保存失败,下次启动需要重新登录")
|
|
283
|
+
end
|
|
284
|
+
update_token_everywhere
|
|
285
|
+
return true
|
|
281
286
|
else
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
Logger.instance.fancyinfo_error("登录失败,未能获取有效token")
|
|
288
|
+
return false
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# 带 401 重试的统一请求方法
|
|
293
|
+
# 所有 API 模块应使用此方法发送请求
|
|
294
|
+
# @param method [Symbol] HTTP 方法 (:get, :post, :put, :delete)
|
|
295
|
+
# @param path [String] 请求路径
|
|
296
|
+
# @param opts [Hash] 传递给 http_client 的参数 (params:, body:, timeout: 等)
|
|
297
|
+
# @return [Hash] 响应数据
|
|
298
|
+
def request_with_auth(method, path, **opts)
|
|
299
|
+
response = @http_client.send(method, path, **opts)
|
|
300
|
+
result = JPSClient::Response.new(response)
|
|
290
301
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
# 保存新 token
|
|
296
|
-
@token_manager.save(auth.access_token, auth.username, auth.expires_at)
|
|
297
|
-
update_token_everywhere()
|
|
298
|
-
return true
|
|
299
|
-
else
|
|
300
|
-
Logger.instance.fancyinfo_error("登录失败,未能获取有效token")
|
|
301
|
-
return false
|
|
302
|
-
end
|
|
302
|
+
if result.need_login?
|
|
303
|
+
if do_login(force_login: true)
|
|
304
|
+
response = @http_client.send(method, path, **opts)
|
|
305
|
+
result = JPSClient::Response.new(response)
|
|
303
306
|
end
|
|
304
307
|
end
|
|
308
|
+
|
|
309
|
+
result.to_h
|
|
305
310
|
end
|
|
306
311
|
|
|
307
312
|
private
|
|
@@ -101,6 +101,16 @@ module JPSClient
|
|
|
101
101
|
request(:post, path, body: body, timeout: timeout)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
# PUT 请求
|
|
105
|
+
def put(path, body: nil, timeout: nil)
|
|
106
|
+
request(:put, path, body: body, timeout: timeout)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# DELETE 请求
|
|
110
|
+
def delete(path, params: nil, timeout: nil)
|
|
111
|
+
request(:delete, path, params: params, timeout: timeout)
|
|
112
|
+
end
|
|
113
|
+
|
|
104
114
|
# 更新 token
|
|
105
115
|
def update_token(new_token)
|
|
106
116
|
@token = new_token
|
|
@@ -128,7 +138,7 @@ module JPSClient
|
|
|
128
138
|
req.headers['Content-Type'] = 'application/json'
|
|
129
139
|
req.headers['token'] = @token if @token
|
|
130
140
|
req.params = params if params
|
|
131
|
-
req.body = body.to_json if body &&
|
|
141
|
+
req.body = body.to_json if body && [:post, :put, :patch].include?(method)
|
|
132
142
|
req.options.timeout = actual_timeout if actual_timeout
|
|
133
143
|
end
|
|
134
144
|
|
|
@@ -243,8 +253,14 @@ module JPSClient
|
|
|
243
253
|
@success && (@code.to_s == '0' || @code.to_s == '200')
|
|
244
254
|
end
|
|
245
255
|
|
|
256
|
+
# 20001: 登录token失效
|
|
246
257
|
def need_login?
|
|
247
|
-
@need_login || @code.to_s == '
|
|
258
|
+
@need_login || @code.to_s == '20001'
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# 20011: 没有权限访问项目
|
|
262
|
+
def no_permission?
|
|
263
|
+
@code.to_s == '20011'
|
|
248
264
|
end
|
|
249
265
|
|
|
250
266
|
def to_h
|
data/lib/jpsclient/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jpsclient
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Your Name
|
|
@@ -79,6 +79,20 @@ dependencies:
|
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '0.2'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: logger
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
82
96
|
- !ruby/object:Gem::Dependency
|
|
83
97
|
name: bundler
|
|
84
98
|
requirement: !ruby/object:Gem::Requirement
|