nc-user-terminator 0.1.4__tar.gz → 0.1.6__tar.gz
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.
Potentially problematic release.
This version of nc-user-terminator might be problematic. Click here for more details.
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/PKG-INFO +7 -2
- nc_user_terminator-0.1.6/README.md +5 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_manager/client.py +33 -8
- nc_user_terminator-0.1.6/nc_user_manager/utils.py +77 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/PKG-INFO +7 -2
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/pyproject.toml +2 -1
- nc_user_terminator-0.1.4/README.md +0 -0
- nc_user_terminator-0.1.4/nc_user_manager/utils.py +0 -39
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_manager/__init__.py +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_manager/cache.py +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_manager/exceptions.py +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_manager/models.py +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/SOURCES.txt +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/dependency_links.txt +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/requires.txt +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/top_level.txt +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/setup.cfg +0 -0
- {nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nc-user-terminator
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: OAuth client wrapper for multi-tenant projects
|
|
5
5
|
Author: bw_song
|
|
6
6
|
Author-email: m132777096902@gmail.com
|
|
@@ -8,5 +8,10 @@ Requires-Python: >=3.8
|
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: cachetools==6.2.0
|
|
10
10
|
Dynamic: author-email
|
|
11
|
-
Dynamic: description-content-type
|
|
12
11
|
Dynamic: requires-python
|
|
12
|
+
|
|
13
|
+
# 更新
|
|
14
|
+
+ V0.1.5
|
|
15
|
+
- 新增本地缓存机制
|
|
16
|
+
+ v0.1.6
|
|
17
|
+
- 默认连接地址
|
|
@@ -7,15 +7,15 @@ from exceptions import OAuthError
|
|
|
7
7
|
from utils import request
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
PRODUCT_HEADER = "X-Product-Code"
|
|
11
11
|
DEFAULT_TTL = 3600
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class OAuthClient:
|
|
15
15
|
def __init__(
|
|
16
16
|
self,
|
|
17
|
-
base_url: str,
|
|
18
|
-
|
|
17
|
+
base_url: Optional[str],
|
|
18
|
+
product_code: str,
|
|
19
19
|
redirect_url: Optional[str] = None,
|
|
20
20
|
single_session: bool = False,
|
|
21
21
|
cache: Optional[BaseCache] = None,
|
|
@@ -24,12 +24,14 @@ class OAuthClient:
|
|
|
24
24
|
OAuth 客户端
|
|
25
25
|
|
|
26
26
|
:param base_url: 服务端基础地址 (例如 http://localhost:8000)
|
|
27
|
-
:param
|
|
27
|
+
:param product_code: 产品编码
|
|
28
28
|
:param redirect_url: 可选,重定向地址
|
|
29
29
|
:param single_session: 是否单会话登录
|
|
30
30
|
"""
|
|
31
|
+
if not base_url:
|
|
32
|
+
base_url = "http://172.27.92.74"
|
|
31
33
|
self._base_url = base_url.rstrip("/")
|
|
32
|
-
self.
|
|
34
|
+
self._product_code = product_code
|
|
33
35
|
self._redirect_url = redirect_url
|
|
34
36
|
self._single_session = single_session
|
|
35
37
|
self._cache = cache or MemoryCache(maxsize=10000, ttl=DEFAULT_TTL)
|
|
@@ -43,7 +45,7 @@ class OAuthClient:
|
|
|
43
45
|
return await asyncio.to_thread(request, *args, **kwargs)
|
|
44
46
|
|
|
45
47
|
def _headers(self, extra: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
|
46
|
-
headers = {
|
|
48
|
+
headers = {PRODUCT_HEADER: self._product_code}
|
|
47
49
|
if extra:
|
|
48
50
|
headers.update(extra)
|
|
49
51
|
return headers
|
|
@@ -209,7 +211,8 @@ class OAuthClient:
|
|
|
209
211
|
headers = self._headers({"Authorization": f"Bearer {token}"})
|
|
210
212
|
res_dict = request("GET", f"{self._base_url}/api/me", headers=headers)
|
|
211
213
|
|
|
212
|
-
|
|
214
|
+
if res_dict.get("success", True):
|
|
215
|
+
self._cache_user(token, res_dict)
|
|
213
216
|
return UserResponse(res_dict)
|
|
214
217
|
|
|
215
218
|
async def say_my_name_async(self, token: str) -> UserResponse:
|
|
@@ -224,8 +227,9 @@ class OAuthClient:
|
|
|
224
227
|
|
|
225
228
|
headers = self._headers({"Authorization": f"Bearer {token}"})
|
|
226
229
|
res_dict = await self._arequest("GET", f"{self._base_url}/api/me", headers=headers)
|
|
230
|
+
if res_dict.get("success", True):
|
|
231
|
+
await self._cache_user_async(token, res_dict)
|
|
227
232
|
|
|
228
|
-
await self._cache_user_async(token, res_dict)
|
|
229
233
|
return UserResponse(res_dict)
|
|
230
234
|
|
|
231
235
|
# 刷新过期时间
|
|
@@ -264,3 +268,24 @@ class OAuthClient:
|
|
|
264
268
|
await self._uncache_user_async(token, data)
|
|
265
269
|
return resp
|
|
266
270
|
|
|
271
|
+
async def main():
|
|
272
|
+
def get_redis_url() -> str:
|
|
273
|
+
return f'redis://default:1q2w3e@localhost:6379/1'
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
import redis.asyncio as redis
|
|
277
|
+
# —— Redis 连接与策略(Bearer + Redis)——
|
|
278
|
+
_redis = redis.from_url(get_redis_url(), decode_responses=True)
|
|
279
|
+
|
|
280
|
+
redis_cache = AsyncRedisCache(_redis)
|
|
281
|
+
|
|
282
|
+
client = OAuthClient("http://auth.nc.com/", "bankgpt", "http://localhost:8000/auth/google/custom_callback", single_session=True, cache=redis_cache)
|
|
283
|
+
authorize = client.authorize("google")
|
|
284
|
+
print(authorize)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
if __name__ == '__main__':
|
|
290
|
+
asyncio.run(main())
|
|
291
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import urllib.parse
|
|
3
|
+
import urllib.request
|
|
4
|
+
import urllib.error
|
|
5
|
+
from typing import Optional, Dict, Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def request(
|
|
9
|
+
method: str,
|
|
10
|
+
url: str,
|
|
11
|
+
params: Optional[Dict[str, Any]] = None,
|
|
12
|
+
headers: Optional[Dict[str, str]] = None,
|
|
13
|
+
json_body: Optional[dict] = None
|
|
14
|
+
) -> dict:
|
|
15
|
+
"""
|
|
16
|
+
发送 HTTP 请求并返回统一结果,不抛异常
|
|
17
|
+
返回格式:
|
|
18
|
+
{
|
|
19
|
+
"status": int, # HTTP 状态码
|
|
20
|
+
"success": bool, # 是否成功 (2xx)
|
|
21
|
+
"body": dict or str, # JSON解析后的body, 或原始body
|
|
22
|
+
"error": Optional[str] # 错误信息
|
|
23
|
+
}
|
|
24
|
+
"""
|
|
25
|
+
# 拼接 GET 参数
|
|
26
|
+
if params:
|
|
27
|
+
query = urllib.parse.urlencode(params)
|
|
28
|
+
url = f"{url}?{query}"
|
|
29
|
+
|
|
30
|
+
data = None
|
|
31
|
+
if json_body is not None:
|
|
32
|
+
data = json.dumps(json_body).encode("utf-8")
|
|
33
|
+
if headers is None:
|
|
34
|
+
headers = {}
|
|
35
|
+
headers["Content-Type"] = "application/json"
|
|
36
|
+
|
|
37
|
+
req = urllib.request.Request(url, method=method.upper(), data=data)
|
|
38
|
+
if headers:
|
|
39
|
+
for k, v in headers.items():
|
|
40
|
+
req.add_header(k, v)
|
|
41
|
+
|
|
42
|
+
result = {
|
|
43
|
+
"status": 0,
|
|
44
|
+
"success": False,
|
|
45
|
+
"message": None,
|
|
46
|
+
"error": None
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
with urllib.request.urlopen(req) as resp:
|
|
51
|
+
body = resp.read().decode()
|
|
52
|
+
if not body:
|
|
53
|
+
return {}
|
|
54
|
+
try:
|
|
55
|
+
return json.loads(body)
|
|
56
|
+
except json.JSONDecodeError:
|
|
57
|
+
result["status"] = 200
|
|
58
|
+
result["success"] = False
|
|
59
|
+
result["message"] = body
|
|
60
|
+
result["error"] = "Invalid JSON response"
|
|
61
|
+
return result
|
|
62
|
+
except urllib.error.HTTPError as e:
|
|
63
|
+
body = e.read().decode() if e.fp else None
|
|
64
|
+
result["status"] = e.code
|
|
65
|
+
try:
|
|
66
|
+
result["message"] = json.loads(body) if body else None
|
|
67
|
+
except json.JSONDecodeError:
|
|
68
|
+
result["message"] = body
|
|
69
|
+
result["error"] = "HTTPError"
|
|
70
|
+
result["success"] = False
|
|
71
|
+
except urllib.error.URLError as e:
|
|
72
|
+
result["status"] = 0
|
|
73
|
+
result["message"] = None
|
|
74
|
+
result["error"] = f"NetworkError: {e.reason}"
|
|
75
|
+
result["success"] = False
|
|
76
|
+
|
|
77
|
+
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nc-user-terminator
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: OAuth client wrapper for multi-tenant projects
|
|
5
5
|
Author: bw_song
|
|
6
6
|
Author-email: m132777096902@gmail.com
|
|
@@ -8,5 +8,10 @@ Requires-Python: >=3.8
|
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: cachetools==6.2.0
|
|
10
10
|
Dynamic: author-email
|
|
11
|
-
Dynamic: description-content-type
|
|
12
11
|
Dynamic: requires-python
|
|
12
|
+
|
|
13
|
+
# 更新
|
|
14
|
+
+ V0.1.5
|
|
15
|
+
- 新增本地缓存机制
|
|
16
|
+
+ v0.1.6
|
|
17
|
+
- 默认连接地址
|
|
@@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nc-user-terminator"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.6"
|
|
8
8
|
description = "OAuth client wrapper for multi-tenant projects"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "bw_song" }
|
|
11
11
|
]
|
|
12
|
+
readme = "README.md"
|
|
12
13
|
requires-python = ">=3.8"
|
|
13
14
|
dependencies = [
|
|
14
15
|
"cachetools==6.2.0"
|
|
File without changes
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import urllib.parse
|
|
3
|
-
import urllib.request
|
|
4
|
-
import urllib.error
|
|
5
|
-
|
|
6
|
-
from exceptions import OAuthError
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def request(method: str, url: str, params=None, headers=None, json_body=None) -> dict:
|
|
10
|
-
# 拼接 GET 参数
|
|
11
|
-
if params:
|
|
12
|
-
query = urllib.parse.urlencode(params)
|
|
13
|
-
url = f"{url}?{query}"
|
|
14
|
-
|
|
15
|
-
data = None
|
|
16
|
-
if json_body is not None:
|
|
17
|
-
data = json.dumps(json_body).encode("utf-8")
|
|
18
|
-
if headers is None:
|
|
19
|
-
headers = {}
|
|
20
|
-
headers["Content-Type"] = "application/json"
|
|
21
|
-
|
|
22
|
-
req = urllib.request.Request(url, method=method.upper(), data=data)
|
|
23
|
-
|
|
24
|
-
if headers:
|
|
25
|
-
for k, v in headers.items():
|
|
26
|
-
req.add_header(k, v)
|
|
27
|
-
|
|
28
|
-
try:
|
|
29
|
-
with urllib.request.urlopen(req) as resp:
|
|
30
|
-
body = resp.read().decode()
|
|
31
|
-
try:
|
|
32
|
-
return json.loads(body)
|
|
33
|
-
except json.JSONDecodeError:
|
|
34
|
-
raise OAuthError("Invalid JSON response", resp.getcode(), body)
|
|
35
|
-
except urllib.error.HTTPError as e:
|
|
36
|
-
body = e.read().decode() if e.fp else None
|
|
37
|
-
raise OAuthError("HTTP request failed", e.code, body)
|
|
38
|
-
except urllib.error.URLError as e:
|
|
39
|
-
raise OAuthError(f"Network error: {e.reason}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/requires.txt
RENAMED
|
File without changes
|
{nc_user_terminator-0.1.4 → nc_user_terminator-0.1.6}/nc_user_terminator.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|