cmdbox 0.5.4__py3-none-any.whl → 0.6.0.1__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.
Potentially problematic release.
This version of cmdbox might be problematic. Click here for more details.
- cmdbox/app/auth/signin.py +463 -303
- cmdbox/app/common.py +51 -3
- cmdbox/app/commons/loghandler.py +62 -13
- cmdbox/app/edge.py +5 -173
- cmdbox/app/edge_tool.py +177 -0
- cmdbox/app/feature.py +10 -9
- cmdbox/app/features/cli/agent_base.py +479 -0
- cmdbox/app/features/cli/audit_base.py +17 -5
- cmdbox/app/features/cli/cmdbox_audit_search.py +24 -1
- cmdbox/app/features/cli/cmdbox_client_file_download.py +1 -1
- cmdbox/app/features/cli/cmdbox_cmd_list.py +105 -0
- cmdbox/app/features/cli/cmdbox_cmd_load.py +104 -0
- cmdbox/app/features/cli/cmdbox_edge_config.py +2 -2
- cmdbox/app/features/cli/cmdbox_edge_start.py +1 -1
- cmdbox/app/features/cli/cmdbox_gui_start.py +9 -132
- cmdbox/app/features/cli/cmdbox_gui_stop.py +4 -21
- cmdbox/app/features/cli/cmdbox_server_start.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_apikey_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_apikey_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_genpass.py +0 -3
- cmdbox/app/features/cli/cmdbox_web_group_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_edit.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_list.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_start.py +120 -104
- cmdbox/app/features/cli/cmdbox_web_stop.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_edit.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_list.py +1 -1
- cmdbox/app/features/web/cmdbox_web_agent.py +262 -0
- cmdbox/app/features/web/cmdbox_web_do_signout.py +3 -3
- cmdbox/app/features/web/cmdbox_web_exec_cmd.py +8 -3
- cmdbox/app/features/web/cmdbox_web_signin.py +5 -4
- cmdbox/app/features/web/cmdbox_web_users.py +2 -0
- cmdbox/app/options.py +62 -9
- cmdbox/app/web.py +139 -15
- cmdbox/extensions/features.yml +18 -0
- cmdbox/extensions/sample_project/sample/app/features/cli/__init__.py +0 -0
- cmdbox/extensions/sample_project/sample/app/features/web/__init__.py +0 -0
- cmdbox/extensions/sample_project/sample/extensions/features.yml +18 -0
- cmdbox/extensions/sample_project/sample/extensions/user_list.yml +2 -1
- cmdbox/extensions/sample_project/sample/logconf_sample.yml +14 -1
- cmdbox/extensions/user_list.yml +1 -0
- cmdbox/licenses/LICENSE.Authlib.1.5.2(BSD License).txt +29 -0
- cmdbox/licenses/LICENSE.Deprecated.1.2.18(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.SQLAlchemy.2.0.40(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.aiohappyeyeballs.2.6.1(Python Software Foundation License).txt +279 -0
- cmdbox/licenses/LICENSE.aiohttp.3.11.18(Apache Software License).txt +13 -0
- cmdbox/licenses/LICENSE.aiosignal.1.3.2(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.attrs.25.3.0(UNKNOWN).txt +21 -0
- cmdbox/licenses/LICENSE.cachetools.5.5.2(MIT License).txt +20 -0
- cmdbox/licenses/LICENSE.distro.1.9.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.docstring_parser.0.16(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.filelock.3.18.0(The Unlicense (Unlicense)).txt +24 -0
- cmdbox/licenses/LICENSE.frozenlist.1.6.0(Apache-2.0).txt +201 -0
- cmdbox/licenses/LICENSE.fsspec.2025.3.2(BSD License).txt +29 -0
- cmdbox/licenses/LICENSE.google-adk.0.5.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-api-python-client.2.169.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.google-auth-httplib2.0.2.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.google-auth.2.40.1(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.google-cloud-aiplatform.1.92.0(Apache 2.0).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-bigquery.3.31.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-core.2.4.3(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-resource-manager.1.14.2(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-secret-manager.2.23.3(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-speech.2.32.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-storage.2.19.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-trace.1.16.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-crc32c.1.7.1(Apache 2.0).txt +202 -0
- cmdbox/licenses/LICENSE.google-genai.1.14.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-resumable-media.2.7.2(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.googleapis-common-protos.1.70.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.graphviz.0.20.3(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.grpc-google-iam-v1.0.14.2(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.grpcio-status.1.71.0(Apache Software License).txt +610 -0
- cmdbox/licenses/LICENSE.grpcio.1.71.0(Apache Software License).txt +610 -0
- cmdbox/licenses/LICENSE.httpcore.1.0.9(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.httplib2.0.22.0(MIT License).txt +23 -0
- cmdbox/licenses/LICENSE.httpx-sse.0.4.0(MIT).txt +21 -0
- cmdbox/licenses/LICENSE.httpx.0.28.1(BSD License).txt +12 -0
- cmdbox/licenses/LICENSE.huggingface-hub.0.31.1(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.importlib_metadata.8.6.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.jiter.0.9.0(MIT License).txt +1 -0
- cmdbox/licenses/LICENSE.jsonschema-specifications.2025.4.1(UNKNOWN).txt +19 -0
- cmdbox/licenses/LICENSE.jsonschema.4.23.0(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.litellm.1.69.0(MIT License).txt +26 -0
- cmdbox/licenses/LICENSE.mcp.1.8.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.multidict.6.4.3(Apache Software License).txt +13 -0
- cmdbox/licenses/LICENSE.openai.1.75.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-api.1.33.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-exporter-gcp-trace.1.9.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-resourcedetector-gcp.1.9.0a0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-sdk.1.33.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-semantic-conventions.0.54b0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.propcache.0.3.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.proto-plus.1.26.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.protobuf.5.29.4(3-Clause BSD License).txt +32 -0
- cmdbox/licenses/LICENSE.pyasn1.0.6.1(BSD License).txt +24 -0
- cmdbox/licenses/LICENSE.pyasn1_modules.0.4.2(BSD License).txt +24 -0
- cmdbox/licenses/LICENSE.pydantic-settings.2.9.1(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.pyparsing.3.2.3(MIT License).txt +18 -0
- cmdbox/licenses/LICENSE.python-dateutil.2.9.0.post0(Apache Software License; BSD License).txt +54 -0
- cmdbox/licenses/LICENSE.referencing.0.36.2(UNKNOWN).txt +19 -0
- cmdbox/licenses/LICENSE.regex.2024.11.6(Apache Software License).txt +208 -0
- cmdbox/licenses/LICENSE.rpds-py.0.24.0(MIT).txt +19 -0
- cmdbox/licenses/LICENSE.rsa.4.9.1(Apache Software License).txt +13 -0
- cmdbox/licenses/LICENSE.shapely.2.1.0(BSD License).txt +29 -0
- cmdbox/licenses/LICENSE.sse-starlette.2.3.4(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.tiktoken.0.9.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.tokenizers.0.21.1(Apache Software License).txt +1 -0
- cmdbox/licenses/LICENSE.tqdm.4.67.1(MIT License; Mozilla Public License 2.0 (MPL 2.0)).txt +49 -0
- cmdbox/licenses/LICENSE.tzlocal.5.3.1(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.uritemplate.4.1.1(Apache Software License; BSD License).txt +3 -0
- cmdbox/licenses/LICENSE.wrapt.1.17.2(BSD License).txt +24 -0
- cmdbox/licenses/LICENSE.yarl.1.20.0(Apache Software License).txt +202 -0
- cmdbox/licenses/files.txt +104 -11
- cmdbox/logconf_audit.yml +16 -3
- cmdbox/logconf_client.yml +16 -3
- cmdbox/logconf_cmdbox.yml +16 -3
- cmdbox/logconf_edge.yml +16 -3
- cmdbox/logconf_gui.yml +15 -2
- cmdbox/logconf_server.yml +15 -2
- cmdbox/logconf_web.yml +15 -2
- cmdbox/version.py +3 -2
- cmdbox/web/agent.html +263 -0
- cmdbox/web/assets/cmdbox/agent.js +338 -0
- cmdbox/web/assets/cmdbox/common.js +1111 -1020
- cmdbox/web/assets/cmdbox/main.js +17 -3
- cmdbox/web/assets/cmdbox/signin.js +4 -4
- cmdbox/web/assets/filer/filer.js +4 -2
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/METADATA +69 -26
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/RECORD +148 -67
- /cmdbox/licenses/{LICENSE.charset-normalizer.3.4.1(MIT License).txt → LICENSE.charset-normalizer.3.4.2(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.click.8.1.8(BSD License).txt → LICENSE.click.8.2.0(UNKNOWN).txt} +0 -0
- /cmdbox/licenses/{LICENSE.cryptography.44.0.2(Apache Software License; BSD License).txt → LICENSE.cryptography.44.0.3(Apache Software License; BSD License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.importlib_metadata.8.7.0(Apache Software License).txt → LICENSE.google-api-core.2.24.2(Apache Software License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.greenlet.3.2.1(MIT AND Python-2.0).txt → LICENSE.greenlet.3.2.2(MIT AND Python-2.0).txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg-binary.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE.psycopg-binary.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE.psycopg.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic.2.11.3(MIT License).txt → LICENSE.pydantic.2.11.4(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic_core.2.33.1(MIT License).txt → LICENSE.pydantic_core.2.33.2(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.redis.5.2.1(MIT License).txt → LICENSE.redis.6.0.0(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.snowballstemmer.2.2.0(BSD License).txt → LICENSE.snowballstemmer.3.0.1(BSD License).txt} +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/LICENSE +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/WHEEL +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/entry_points.txt +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/top_level.txt +0 -0
cmdbox/app/common.py
CHANGED
|
@@ -4,6 +4,8 @@ from cmdbox.app.commons import convert, module, loghandler
|
|
|
4
4
|
from cryptography.fernet import Fernet
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from pkg_resources import resource_string
|
|
7
|
+
from rich.logging import RichHandler
|
|
8
|
+
from rich.console import Console
|
|
7
9
|
from tabulate import tabulate
|
|
8
10
|
from typing import List, Tuple, Dict, Any
|
|
9
11
|
import asyncio
|
|
@@ -91,6 +93,53 @@ def save_yml(yml_path:Path, data:dict) -> None:
|
|
|
91
93
|
with open(yml_path, 'w') as f:
|
|
92
94
|
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
|
93
95
|
|
|
96
|
+
def reset_logger(name:str, stderr:bool=False, fmt:str='[%(asctime)s] %(levelname)s - %(message)s', datefmt:str='%Y-%m-%d %H:%M:%S') -> None:
|
|
97
|
+
"""
|
|
98
|
+
指定されたロガーのハンドラをクリアし、新しいハンドラを追加します。
|
|
99
|
+
Args:
|
|
100
|
+
name (str): ロガーの名前
|
|
101
|
+
stderr (bool, optional): 標準エラー出力を使用するかどうか. Defaults to False.
|
|
102
|
+
fmt (str, optional): ログフォーマット. Defaults to '[%(asctime)s] %(levelname)s - %(message)s'.
|
|
103
|
+
datefmt (str, optional): 日時フォーマット. Defaults to '%Y-%m-%d %H:%M:%S'.
|
|
104
|
+
"""
|
|
105
|
+
logger = logging.getLogger(name)
|
|
106
|
+
logger.handlers.clear()
|
|
107
|
+
logger.propagate = False
|
|
108
|
+
logger.addHandler(create_log_handler(stderr, fmt, datefmt))
|
|
109
|
+
|
|
110
|
+
def create_log_handler(stderr:bool=False, fmt:str='[%(asctime)s] %(levelname)s - %(message)s', datefmt:str='%Y-%m-%d %H:%M:%S') -> logging.Handler:
|
|
111
|
+
"""
|
|
112
|
+
ログハンドラを生成します。
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
stderr (bool, optional): 標準エラー出力を使用するかどうか. Defaults to False.
|
|
116
|
+
fmt (str, optional): ログフォーマット. Defaults to '[%(asctime)s] %(levelname)s - %(message)s'.
|
|
117
|
+
datefmt (str, optional): 日時フォーマット. Defaults to '%Y-%m-%d %H:%M:%S'.
|
|
118
|
+
Returns:
|
|
119
|
+
logging.Handler: ログハンドラ
|
|
120
|
+
"""
|
|
121
|
+
formatter = logging.Formatter(fmt, datefmt)
|
|
122
|
+
#handler = RichHandler(console=Console(stderr=stderr), show_path=False, omit_repeated_times=False,
|
|
123
|
+
# tracebacks_word_wrap=False, log_time_format='[%Y-%m-%d %H:%M]')
|
|
124
|
+
handler = loghandler.ColorfulStreamHandler(sys.stdout if not stderr else sys.stderr)
|
|
125
|
+
handler.setFormatter(formatter)
|
|
126
|
+
return handler
|
|
127
|
+
|
|
128
|
+
def create_console(stderr:bool=False, file=None) -> Console:
|
|
129
|
+
"""
|
|
130
|
+
コンソールを生成します。
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
stderr (bool, optional): 標準エラー出力を使用するかどうか. Defaults to False.
|
|
134
|
+
file (file, optional): 出力先のファイル. Defaults to None.
|
|
135
|
+
Returns:
|
|
136
|
+
Console: コンソール
|
|
137
|
+
"""
|
|
138
|
+
console = Console(height=False,
|
|
139
|
+
soft_wrap=False, stderr=stderr, file=file, log_time=True, log_path=False, log_time_format='[%Y-%m-%d %H:%M:%S]')
|
|
140
|
+
#console = Console(soft_wrap=True, stderr=stderr, file=file, log_time=True, log_path=False, log_time_format='[%Y-%m-%d %H:%M:%S]')
|
|
141
|
+
return console
|
|
142
|
+
|
|
94
143
|
def default_logger(debug:bool=False, ver=version, webcall:bool=False) -> logging.Logger:
|
|
95
144
|
"""
|
|
96
145
|
デフォルトのロガーを生成します。
|
|
@@ -105,12 +154,11 @@ def default_logger(debug:bool=False, ver=version, webcall:bool=False) -> logging
|
|
|
105
154
|
"""
|
|
106
155
|
logger = logging.getLogger(ver.__appid__)
|
|
107
156
|
if not webcall:
|
|
108
|
-
|
|
109
|
-
handler = loghandler.ColorfulStreamHandler(sys.stdout)
|
|
157
|
+
handler = create_log_handler()
|
|
110
158
|
handler.setLevel(logging.DEBUG if debug else logging.INFO)
|
|
111
|
-
handler.setFormatter(formatter)
|
|
112
159
|
logger.addHandler(handler)
|
|
113
160
|
logger.setLevel(logging.DEBUG if debug else logging.INFO)
|
|
161
|
+
logger.propagate = False
|
|
114
162
|
return logger
|
|
115
163
|
|
|
116
164
|
def load_config(mode:str, debug:bool=False, data=HOME_DIR, webcall:bool=False, ver=version) -> Tuple[logging.Logger, dict]:
|
cmdbox/app/commons/loghandler.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
from rich import highlighter
|
|
3
|
+
from rich.theme import Theme
|
|
1
4
|
import re
|
|
2
5
|
import logging
|
|
3
6
|
import logging.handlers
|
|
@@ -77,25 +80,71 @@ def colorize_msg(msg) -> str:
|
|
|
77
80
|
return msg
|
|
78
81
|
|
|
79
82
|
level_mapping = {
|
|
80
|
-
logging.DEBUG: f"{colorize('DEBUG', Colors.Bold, Colors.Cyan)}
|
|
81
|
-
logging.INFO: f"{colorize('INFO', Colors.Bold, Colors.Green)}
|
|
82
|
-
logging.WARNING: f"{colorize('
|
|
83
|
-
logging.ERROR: f"{colorize('ERROR', Colors.Bold, Colors.Red)}
|
|
84
|
-
logging.CRITICAL:f"{colorize('
|
|
83
|
+
logging.DEBUG: f"{colorize('DEBUG', Colors.Bold, Colors.Cyan)}",
|
|
84
|
+
logging.INFO: f"{colorize('INFO', Colors.Bold, Colors.Green)} ",
|
|
85
|
+
logging.WARNING: f"{colorize('WARN', Colors.Bold, Colors.Yellow)} ",
|
|
86
|
+
logging.ERROR: f"{colorize('ERROR', Colors.Bold, Colors.Red)}",
|
|
87
|
+
logging.CRITICAL:f"{colorize('FATAL', Colors.Bold, Colors.LightGray, Colors.BackgroundRed)}"}
|
|
85
88
|
|
|
86
89
|
level_mapping_nc = {
|
|
87
|
-
logging.DEBUG: f"DEBUG
|
|
88
|
-
logging.INFO: f"INFO
|
|
89
|
-
logging.WARNING: f"
|
|
90
|
-
logging.ERROR: f"ERROR
|
|
91
|
-
logging.CRITICAL:f"
|
|
90
|
+
logging.DEBUG: f"DEBUG",
|
|
91
|
+
logging.INFO: f"INFO ",
|
|
92
|
+
logging.WARNING: f"WARN ",
|
|
93
|
+
logging.ERROR: f"ERROR",
|
|
94
|
+
logging.CRITICAL:f"FATAL"}
|
|
95
|
+
|
|
96
|
+
theme=Theme({
|
|
97
|
+
"repr.log_debug": "bold cyan",
|
|
98
|
+
"repr.log_info": "bold green",
|
|
99
|
+
"repr.log_warn": "bold Yellow",
|
|
100
|
+
"repr.log_error": "bold red",
|
|
101
|
+
"repr.log_fatal": "bold red reverse",
|
|
102
|
+
"repr.log_product": "dodger_blue2 reverse",
|
|
103
|
+
"repr.log_success": "green",})
|
|
104
|
+
|
|
105
|
+
class LogLevelHighlighter(highlighter.ReprHighlighter):
|
|
106
|
+
def __init__(self):
|
|
107
|
+
#self.highlights = []
|
|
108
|
+
self.highlights.append(r"(?P<log_debug>DEBUG)")
|
|
109
|
+
self.highlights.append(r"(?P<log_info>INFO)")
|
|
110
|
+
self.highlights.append(r"(?P<log_warn>WARN|WARNING|WARN|CAUTION|NOTICE|STOP|DISCONNECTED|DENY)")
|
|
111
|
+
self.highlights.append(r"(?P<log_error>ERROR|ALERT|ABORT|FAILED)")
|
|
112
|
+
self.highlights.append(r"(?P<log_fatal>FATAL|CRITICAL)")
|
|
113
|
+
self.highlights.append(r"(?P<log_product>CMDBOX|IINFER|USOUND|GAIAN|GAIC|WITSHAPE)")
|
|
114
|
+
self.highlights.append(r"(?P<log_success>SUCCESS|OK|PASSED|DONE|COMPLETE|START|FINISH|OPEN|CONNECTED|ALLOW|EXEC)")
|
|
115
|
+
"""
|
|
116
|
+
self.highlights.append(r"(?P<tag_start><)(?P<tag_name>[-\w.:|]*)(?P<tag_contents>[\w\W]*)(?P<tag_end>>)")
|
|
117
|
+
self.highlights.append(r'(?P<attrib_name>[\w_]{1,50})=(?P<attrib_value>"?[\w_]+"?)?')
|
|
118
|
+
self.highlights.append(r"(?P<brace>[][{}()])")
|
|
119
|
+
self.highlights.append(highlighter._combine_regex(
|
|
120
|
+
r"(?P<ipv4>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})",
|
|
121
|
+
r"(?P<ipv6>([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})",
|
|
122
|
+
r"(?P<eui64>(?:[0-9A-Fa-f]{1,2}-){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){3}[0-9A-Fa-f]{4})",
|
|
123
|
+
r"(?P<eui48>(?:[0-9A-Fa-f]{1,2}-){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})",
|
|
124
|
+
r"(?P<uuid>[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})",
|
|
125
|
+
r"(?P<call>[\w.]*?)\(",
|
|
126
|
+
r"\b(?P<bool_true>True)\b|\b(?P<bool_false>False)\b|\b(?P<none>None)\b",
|
|
127
|
+
r"(?P<ellipsis>\.\.\.)",
|
|
128
|
+
r"(?P<number_complex>(?<!\w)(?:\-?[0-9]+\.?[0-9]*(?:e[-+]?\d+?)?)(?:[-+](?:[0-9]+\.?[0-9]*(?:e[-+]?\d+)?))?j)",
|
|
129
|
+
r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[-+]?\d+?)?\b|0x[0-9a-fA-F]*)",
|
|
130
|
+
r"(?P<path>\B(/[-\w._+]+)*\/)(?P<filename>[-\w._+]*)?",
|
|
131
|
+
r"(?<![\\\w])(?P<str>b?'''.*?(?<!\\)'''|b?'.*?(?<!\\)'|b?\"\"\".*?(?<!\\)\"\"\"|b?\".*?(?<!\\)\")",
|
|
132
|
+
r"(?P<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~@]*)",
|
|
133
|
+
))
|
|
134
|
+
"""
|
|
135
|
+
self.highlights = [re.compile(h, re.IGNORECASE) for h in self.highlights]
|
|
92
136
|
|
|
93
137
|
class ColorfulStreamHandler(logging.StreamHandler):
|
|
138
|
+
console = Console(soft_wrap=True, height=True, highlighter=LogLevelHighlighter(), theme=theme)
|
|
139
|
+
|
|
94
140
|
def emit(self, record: logging.LogRecord) -> None:
|
|
95
|
-
record.levelname = level_mapping[record.levelno]
|
|
141
|
+
#record.levelname = level_mapping[record.levelno]
|
|
96
142
|
#record.asctime = colorize(record.asctime, Colors.Bold)
|
|
97
|
-
record.msg = colorize_msg(record.msg)
|
|
98
|
-
super().emit(record)
|
|
143
|
+
#record.msg = colorize_msg(record.msg)
|
|
144
|
+
#super().emit(record)
|
|
145
|
+
record.levelname = level_mapping_nc[record.levelno]
|
|
146
|
+
record.msg = self.format(record)
|
|
147
|
+
self.console.print(record.msg)
|
|
99
148
|
|
|
100
149
|
class TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
|
|
101
150
|
def emit(self, record: logging.LogRecord) -> None:
|
cmdbox/app/edge.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from cmdbox.app import common, feature, options, web
|
|
2
|
-
from cmdbox.app.auth import signin, signin_saml, azure_signin, azure_signin_saml, github_signin, google_signin
|
|
1
|
+
from cmdbox.app import common, edge_tool, feature, options, web
|
|
3
2
|
from cmdbox.app.commons import convert
|
|
4
3
|
from cmdbox.app.options import Options
|
|
5
4
|
from fastapi import FastAPI, Request, HTTPException
|
|
@@ -27,7 +26,7 @@ class Edge(object):
|
|
|
27
26
|
self.appcls = appcls
|
|
28
27
|
self.ver = ver
|
|
29
28
|
self.options = options.Options.getInstance()
|
|
30
|
-
self.tool = Tool(logger, appcls, ver)
|
|
29
|
+
self.tool = edge_tool.Tool(logger, appcls, ver)
|
|
31
30
|
if self.ver is None:
|
|
32
31
|
raise ValueError('ver is None')
|
|
33
32
|
if self.appcls is None:
|
|
@@ -298,7 +297,7 @@ class Edge(object):
|
|
|
298
297
|
def _job(thevent:threading.Event, pipe_cmd, prevq:queue.Queue):
|
|
299
298
|
resq:queue.Queue = pipe_cmd['resq']
|
|
300
299
|
del pipe_cmd['resq']
|
|
301
|
-
tool = Tool(self.logger, self.appcls, self.ver)
|
|
300
|
+
tool = edge_tool.Tool(self.logger, self.appcls, self.ver)
|
|
302
301
|
tool.set_session(self.session, self.svcert_no_verify, self.endpoint, self.icon_path, self.user_info, self.oauth2, self.saml)
|
|
303
302
|
feat:feature.Feature = self.options.get_cmd_attr(pipe_cmd['mode'], pipe_cmd['cmd'], 'feature')
|
|
304
303
|
while not thevent.is_set():
|
|
@@ -355,7 +354,7 @@ class Edge(object):
|
|
|
355
354
|
for opt in opts:
|
|
356
355
|
def mkcmd(opt):
|
|
357
356
|
def _ex():
|
|
358
|
-
tool = Tool(self.logger, self.appcls, self.ver)
|
|
357
|
+
tool = edge_tool.Tool(self.logger, self.appcls, self.ver)
|
|
359
358
|
tool.set_session(self.session, self.svcert_no_verify, self.endpoint, self.icon_path, self.user_info, self.oauth2, self.saml)
|
|
360
359
|
feat:feature.Feature = self.options.get_cmd_attr(opt['mode'], opt['cmd'], 'feature')
|
|
361
360
|
for status, ret in feat.edgerun(opt, tool, self.logger, self.timeout):
|
|
@@ -380,7 +379,7 @@ class Edge(object):
|
|
|
380
379
|
items = []
|
|
381
380
|
items.append(pystray.MenuItem('Gui', lambda: self.tool.open_browser('/gui')))
|
|
382
381
|
for k, op in opens.items():
|
|
383
|
-
def mkop(tool:Tool, href):
|
|
382
|
+
def mkop(tool:edge_tool.Tool, href):
|
|
384
383
|
return lambda: tool.open_browser(href)
|
|
385
384
|
items.append(pystray.MenuItem(op['html'], mkop(self.tool, op['href'])))
|
|
386
385
|
return items
|
|
@@ -754,170 +753,3 @@ class Edge(object):
|
|
|
754
753
|
return 0, dict(success="Signin success.")
|
|
755
754
|
|
|
756
755
|
return 1, dict(warn="unsupported auth_type.")
|
|
757
|
-
|
|
758
|
-
class Tool(object):
|
|
759
|
-
def __init__(self, logger:logging.Logger, appcls=None, ver=None):
|
|
760
|
-
self.logger = logger
|
|
761
|
-
self.appcls = appcls
|
|
762
|
-
self.ver = ver
|
|
763
|
-
|
|
764
|
-
def notify(self, message:dict):
|
|
765
|
-
"""
|
|
766
|
-
通知メッセージを表示します
|
|
767
|
-
|
|
768
|
-
Args:
|
|
769
|
-
message (dict): メッセージ
|
|
770
|
-
"""
|
|
771
|
-
if type(message) is list:
|
|
772
|
-
message = message[0]
|
|
773
|
-
if type(message) is not dict:
|
|
774
|
-
message = {"info":str(message)}
|
|
775
|
-
if self.logger.level == logging.DEBUG:
|
|
776
|
-
self.logger.debug(f"notify: {common.to_str(message, slise=256)}")
|
|
777
|
-
try:
|
|
778
|
-
if 'success' in message and type(message['success']) == dict:
|
|
779
|
-
message = "\n".join([f"{k}:{v}" for k, v in message['success'].items()])
|
|
780
|
-
message = f'Success\n{message}'
|
|
781
|
-
else:
|
|
782
|
-
message = "\n".join([f"{k} : {v}" for k, v in message.items()])
|
|
783
|
-
import plyer
|
|
784
|
-
if hasattr(self, 'icon_path') and self.icon_path is not None:
|
|
785
|
-
plyer.notification.notify(title=self.ver.__title__, message=str(message)[:256], app_icon=str(self.icon_path))
|
|
786
|
-
else:
|
|
787
|
-
plyer.notification.notify(title=self.ver.__title__, message=str(message)[:256])
|
|
788
|
-
except Exception as e:
|
|
789
|
-
self.logger.error(f"notify error. {e}", exc_info=True)
|
|
790
|
-
|
|
791
|
-
def set_session(self, session:requests.Session, svcert_no_verify:bool, endpoint:str, icon_path:Path, user_info:Dict[str, Any], oauth2:str, saml:str):
|
|
792
|
-
"""
|
|
793
|
-
セッションを設定します
|
|
794
|
-
|
|
795
|
-
Args:
|
|
796
|
-
session (requests.Session): セッション
|
|
797
|
-
svcert_no_verify (bool): サーバー証明書の検証を行わない
|
|
798
|
-
endpoint (str): エンドポイント
|
|
799
|
-
icon_path (Path): アイコン画像のパス
|
|
800
|
-
user_info (Dict[str, Any]): ユーザー情報
|
|
801
|
-
oauth2 (str): OAuth2
|
|
802
|
-
"""
|
|
803
|
-
self.session = session
|
|
804
|
-
self.svcert_no_verify = svcert_no_verify
|
|
805
|
-
self.endpoint = endpoint
|
|
806
|
-
self.icon_path = icon_path
|
|
807
|
-
self.user = user_info
|
|
808
|
-
self.oauth2 = oauth2
|
|
809
|
-
self.saml = saml
|
|
810
|
-
|
|
811
|
-
def exec_cmd(self, opt:Dict[str, Any], logger:logging.Logger, timeout:int, prevres:Any=None) -> Tuple[int, Dict[str, Any]]:
|
|
812
|
-
"""
|
|
813
|
-
この機能のエッジ側の実行を行います
|
|
814
|
-
|
|
815
|
-
Args:
|
|
816
|
-
opt (Dict[str, Any]): オプション
|
|
817
|
-
logger (logging.Logger): ロガー
|
|
818
|
-
timeout (int): タイムアウト時間
|
|
819
|
-
prevres (Any): 前コマンドの結果。pipeline実行の実行結果を参照する時に使用します。
|
|
820
|
-
|
|
821
|
-
Returns:
|
|
822
|
-
Tuple[int, Dict[str, Any], Any]: 終了コード, 結果
|
|
823
|
-
"""
|
|
824
|
-
if logger.level == logging.DEBUG:
|
|
825
|
-
logger.debug(f"exec_cmd: {self.endpoint}/exec_cmd/{opt['title']}")
|
|
826
|
-
if prevres is not None:
|
|
827
|
-
headers = {'content-type':'application/octet-stream'}
|
|
828
|
-
prevres = common.to_str(prevres)
|
|
829
|
-
res = self.session.post(f"{self.endpoint}/exec_cmd/{opt['title']}", headers=headers, data=prevres,
|
|
830
|
-
verify=not self.svcert_no_verify, timeout=timeout, allow_redirects=False)
|
|
831
|
-
else:
|
|
832
|
-
res = self.session.post(f"{self.endpoint}/exec_cmd/{opt['title']}",
|
|
833
|
-
verify=not self.svcert_no_verify, timeout=timeout, allow_redirects=False)
|
|
834
|
-
|
|
835
|
-
if res.status_code != 200:
|
|
836
|
-
msg = dict(warn=f"Access failed. status_code={res.status_code}")
|
|
837
|
-
logger.warning(f"Access failed. status_code={res.status_code}")
|
|
838
|
-
return 1, msg
|
|
839
|
-
else:
|
|
840
|
-
ret = msg = res.json()
|
|
841
|
-
if isinstance(msg, list):
|
|
842
|
-
if len(msg) == 0:
|
|
843
|
-
logger.warning(f"No result.")
|
|
844
|
-
return 1, dict(warn="No result.")
|
|
845
|
-
msg = msg[0]
|
|
846
|
-
if isinstance(msg, dict) and 'success' not in msg:
|
|
847
|
-
logger.warning(f"{msg}")
|
|
848
|
-
return 1, ret
|
|
849
|
-
if logger.level == logging.DEBUG:
|
|
850
|
-
logger.debug(f"{common.to_str(ret, slise=255)}")
|
|
851
|
-
return 0, ret
|
|
852
|
-
|
|
853
|
-
def pub_result(self, title:str, output:str, timeout:int) -> Tuple[int, Dict[str, Any]]:
|
|
854
|
-
"""
|
|
855
|
-
結果を公開します
|
|
856
|
-
|
|
857
|
-
Args:
|
|
858
|
-
title (str): タイトル
|
|
859
|
-
output (str): 出力
|
|
860
|
-
logger (logging.Logger): ロガー
|
|
861
|
-
timeout (int): タイムアウト時間
|
|
862
|
-
|
|
863
|
-
Returns:
|
|
864
|
-
Tuple[int, Dict[str, Any]]: 終了コード, メッセージ
|
|
865
|
-
"""
|
|
866
|
-
output = common.to_str(output)
|
|
867
|
-
data = f'title={urllib.parse.quote(title)}&output={urllib.parse.quote(output)}'
|
|
868
|
-
headers = {'content-type':'application/x-www-form-urlencoded'}
|
|
869
|
-
res = self.session.post(f"{self.endpoint}/result/pub", headers=headers, data=data,
|
|
870
|
-
verify=not self.svcert_no_verify, timeout=timeout, allow_redirects=False)
|
|
871
|
-
if res.status_code != 200:
|
|
872
|
-
msg = dict(warn=f"Access failed. status_code={res.status_code}")
|
|
873
|
-
return 1, msg
|
|
874
|
-
else:
|
|
875
|
-
msg = res.json()
|
|
876
|
-
return 0, msg
|
|
877
|
-
|
|
878
|
-
def open_browser(self, path:str) -> Tuple[int, Dict[str, str]]:
|
|
879
|
-
"""
|
|
880
|
-
指定したパスをブラウザで開きます。
|
|
881
|
-
この時認証情報を含めて開きます。
|
|
882
|
-
|
|
883
|
-
Args:
|
|
884
|
-
path (str): パス
|
|
885
|
-
|
|
886
|
-
Returns:
|
|
887
|
-
Tuple[int, Dict[str, str]]: 終了コード, メッセージ
|
|
888
|
-
"""
|
|
889
|
-
path = f"/{path}" if not path.startswith('/') else path
|
|
890
|
-
if not hasattr(self, 'user'):
|
|
891
|
-
webbrowser.open(f"{self.endpoint}{path}")
|
|
892
|
-
return 0, dict(success="Open browser.")
|
|
893
|
-
token = dict(auth_type=self.user['auth_type'])
|
|
894
|
-
if self.user['auth_type'] == "noauth":
|
|
895
|
-
webbrowser.open(f"{self.endpoint}{path}")
|
|
896
|
-
return 0, dict(success="Open browser.")
|
|
897
|
-
elif self.user['auth_type'] == "idpw":
|
|
898
|
-
hashed = self.user['password'] if self.user['hash']=='plain' else common.hash_password(self.user['password'], self.user['hash'])
|
|
899
|
-
token = dict(**token, **dict(user=self.user['name'], token=common.encrypt(path, hashed)))
|
|
900
|
-
token = convert.str2b64str(common.to_str(token))
|
|
901
|
-
webbrowser.open(f"{self.endpoint}/dosignin_token/{token}{path}")
|
|
902
|
-
return 0, dict(success="Open browser.")
|
|
903
|
-
elif self.user['auth_type'] == "apikey":
|
|
904
|
-
hashed = common.hash_password(self.user['apikey'], 'sha1')
|
|
905
|
-
token = dict(**token, **dict(user=self.user['name'], token=common.encrypt(path, hashed)))
|
|
906
|
-
token = convert.str2b64str(common.to_str(token))
|
|
907
|
-
webbrowser.open(f"{self.endpoint}/dosignin_token/{token}{path}")
|
|
908
|
-
return 0, dict(success="Open browser.")
|
|
909
|
-
elif self.user['auth_type'] == "oauth2":
|
|
910
|
-
if self.oauth2 == 'google':
|
|
911
|
-
webbrowser.open(f"{self.endpoint}/oauth2/google/session/{self.user['access_token']}{path}")
|
|
912
|
-
return 0, dict(success="Open browser.")
|
|
913
|
-
if self.oauth2 == 'github':
|
|
914
|
-
webbrowser.open(f"{self.endpoint}/oauth2/github/session/{self.user['access_token']}{path}")
|
|
915
|
-
return 0, dict(success="Open browser.")
|
|
916
|
-
if self.oauth2 == 'azure':
|
|
917
|
-
webbrowser.open(f"{self.endpoint}/oauth2/azure/session/{self.user['access_token']}{path}")
|
|
918
|
-
return 0, dict(success="Open browser.")
|
|
919
|
-
elif self.user['auth_type'] == "saml":
|
|
920
|
-
if self.saml == 'azure':
|
|
921
|
-
webbrowser.open(f"{self.endpoint}/saml/azure/session/{self.user['saml_token']}{path}")
|
|
922
|
-
return 0, dict(success="Open browser.")
|
|
923
|
-
return 1, dict(warn="unsupported auth_type.")
|
cmdbox/app/edge_tool.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from cmdbox.app import common
|
|
2
|
+
from cmdbox.app.commons import convert
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Any, Tuple
|
|
5
|
+
import logging
|
|
6
|
+
import requests
|
|
7
|
+
import webbrowser
|
|
8
|
+
import urllib.parse
|
|
9
|
+
import urllib3
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Tool(object):
|
|
13
|
+
def __init__(self, logger:logging.Logger, appcls=None, ver=None):
|
|
14
|
+
self.logger = logger
|
|
15
|
+
self.appcls = appcls
|
|
16
|
+
self.ver = ver
|
|
17
|
+
|
|
18
|
+
def notify(self, message:dict):
|
|
19
|
+
"""
|
|
20
|
+
通知メッセージを表示します
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
message (dict): メッセージ
|
|
24
|
+
"""
|
|
25
|
+
if type(message) is list:
|
|
26
|
+
message = message[0]
|
|
27
|
+
if type(message) is not dict:
|
|
28
|
+
message = {"info":str(message)}
|
|
29
|
+
if self.logger.level == logging.DEBUG:
|
|
30
|
+
self.logger.debug(f"notify: {common.to_str(message, slise=256)}")
|
|
31
|
+
try:
|
|
32
|
+
if 'success' in message and type(message['success']) == dict:
|
|
33
|
+
message = "\n".join([f"{k}:{v}" for k, v in message['success'].items()])
|
|
34
|
+
message = f'Success\n{message}'
|
|
35
|
+
else:
|
|
36
|
+
message = "\n".join([f"{k} : {v}" for k, v in message.items()])
|
|
37
|
+
import plyer
|
|
38
|
+
if hasattr(self, 'icon_path') and self.icon_path is not None:
|
|
39
|
+
plyer.notification.notify(title=self.ver.__title__, message=str(message)[:256], app_icon=str(self.icon_path))
|
|
40
|
+
else:
|
|
41
|
+
plyer.notification.notify(title=self.ver.__title__, message=str(message)[:256])
|
|
42
|
+
except Exception as e:
|
|
43
|
+
self.logger.error(f"notify error. {e}", exc_info=True)
|
|
44
|
+
|
|
45
|
+
def set_session(self, session:requests.Session, svcert_no_verify:bool, endpoint:str, icon_path:Path, user_info:Dict[str, Any], oauth2:str, saml:str):
|
|
46
|
+
"""
|
|
47
|
+
セッションを設定します
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
session (requests.Session): セッション
|
|
51
|
+
svcert_no_verify (bool): サーバー証明書の検証を行わない
|
|
52
|
+
endpoint (str): エンドポイント
|
|
53
|
+
icon_path (Path): アイコン画像のパス
|
|
54
|
+
user_info (Dict[str, Any]): ユーザー情報
|
|
55
|
+
oauth2 (str): OAuth2
|
|
56
|
+
"""
|
|
57
|
+
self.session = session
|
|
58
|
+
self.svcert_no_verify = svcert_no_verify
|
|
59
|
+
self.endpoint = endpoint
|
|
60
|
+
self.icon_path = icon_path
|
|
61
|
+
self.user = user_info
|
|
62
|
+
self.oauth2 = oauth2
|
|
63
|
+
self.saml = saml
|
|
64
|
+
|
|
65
|
+
def exec_cmd(self, opt:Dict[str, Any], logger:logging.Logger, timeout:int, prevres:Any=None) -> Tuple[int, Dict[str, Any]]:
|
|
66
|
+
"""
|
|
67
|
+
この機能のエッジ側の実行を行います
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
opt (Dict[str, Any]): オプション
|
|
71
|
+
logger (logging.Logger): ロガー
|
|
72
|
+
timeout (int): タイムアウト時間
|
|
73
|
+
prevres (Any): 前コマンドの結果。pipeline実行の実行結果を参照する時に使用します。
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Tuple[int, Dict[str, Any], Any]: 終了コード, 結果
|
|
77
|
+
"""
|
|
78
|
+
if logger.level == logging.DEBUG:
|
|
79
|
+
logger.debug(f"exec_cmd: {self.endpoint}/exec_cmd/{opt['title']}")
|
|
80
|
+
if prevres is not None:
|
|
81
|
+
headers = {'content-type':'application/octet-stream'}
|
|
82
|
+
prevres = common.to_str(prevres)
|
|
83
|
+
res = self.session.post(f"{self.endpoint}/exec_cmd/{opt['title']}", headers=headers, data=prevres,
|
|
84
|
+
verify=not self.svcert_no_verify, timeout=timeout, allow_redirects=False)
|
|
85
|
+
else:
|
|
86
|
+
res = self.session.post(f"{self.endpoint}/exec_cmd/{opt['title']}",
|
|
87
|
+
verify=not self.svcert_no_verify, timeout=timeout, allow_redirects=False)
|
|
88
|
+
|
|
89
|
+
if res.status_code != 200:
|
|
90
|
+
msg = dict(warn=f"Access failed. status_code={res.status_code}")
|
|
91
|
+
logger.warning(f"Access failed. status_code={res.status_code}")
|
|
92
|
+
return 1, msg
|
|
93
|
+
else:
|
|
94
|
+
ret = msg = res.json()
|
|
95
|
+
if isinstance(msg, list):
|
|
96
|
+
if len(msg) == 0:
|
|
97
|
+
logger.warning(f"No result.")
|
|
98
|
+
return 1, dict(warn="No result.")
|
|
99
|
+
msg = msg[0]
|
|
100
|
+
if isinstance(msg, dict) and 'success' not in msg:
|
|
101
|
+
logger.warning(f"{msg}")
|
|
102
|
+
return 1, ret
|
|
103
|
+
if logger.level == logging.DEBUG:
|
|
104
|
+
logger.debug(f"{common.to_str(ret, slise=255)}")
|
|
105
|
+
return 0, ret
|
|
106
|
+
|
|
107
|
+
def pub_result(self, title:str, output:str, timeout:int) -> Tuple[int, Dict[str, Any]]:
|
|
108
|
+
"""
|
|
109
|
+
結果を公開します
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
title (str): タイトル
|
|
113
|
+
output (str): 出力
|
|
114
|
+
logger (logging.Logger): ロガー
|
|
115
|
+
timeout (int): タイムアウト時間
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Tuple[int, Dict[str, Any]]: 終了コード, メッセージ
|
|
119
|
+
"""
|
|
120
|
+
output = common.to_str(output)
|
|
121
|
+
data = f'title={urllib.parse.quote(title)}&output={urllib.parse.quote(output)}'
|
|
122
|
+
headers = {'content-type':'application/x-www-form-urlencoded'}
|
|
123
|
+
res = self.session.post(f"{self.endpoint}/result/pub", headers=headers, data=data,
|
|
124
|
+
verify=not self.svcert_no_verify, timeout=timeout, allow_redirects=False)
|
|
125
|
+
if res.status_code != 200:
|
|
126
|
+
msg = dict(warn=f"Access failed. status_code={res.status_code}")
|
|
127
|
+
return 1, msg
|
|
128
|
+
else:
|
|
129
|
+
msg = res.json()
|
|
130
|
+
return 0, msg
|
|
131
|
+
|
|
132
|
+
def open_browser(self, path:str) -> Tuple[int, Dict[str, str]]:
|
|
133
|
+
"""
|
|
134
|
+
指定したパスをブラウザで開きます。
|
|
135
|
+
この時認証情報を含めて開きます。
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
path (str): パス
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Tuple[int, Dict[str, str]]: 終了コード, メッセージ
|
|
142
|
+
"""
|
|
143
|
+
path = f"/{path}" if not path.startswith('/') else path
|
|
144
|
+
if not hasattr(self, 'user'):
|
|
145
|
+
webbrowser.open(f"{self.endpoint}{path}")
|
|
146
|
+
return 0, dict(success="Open browser.")
|
|
147
|
+
token = dict(auth_type=self.user['auth_type'])
|
|
148
|
+
if self.user['auth_type'] == "noauth":
|
|
149
|
+
webbrowser.open(f"{self.endpoint}{path}")
|
|
150
|
+
return 0, dict(success="Open browser.")
|
|
151
|
+
elif self.user['auth_type'] == "idpw":
|
|
152
|
+
hashed = self.user['password'] if self.user['hash']=='plain' else common.hash_password(self.user['password'], self.user['hash'])
|
|
153
|
+
token = dict(**token, **dict(user=self.user['name'], token=common.encrypt(path, hashed)))
|
|
154
|
+
token = convert.str2b64str(common.to_str(token))
|
|
155
|
+
webbrowser.open(f"{self.endpoint}/dosignin_token/{token}{path}")
|
|
156
|
+
return 0, dict(success="Open browser.")
|
|
157
|
+
elif self.user['auth_type'] == "apikey":
|
|
158
|
+
hashed = common.hash_password(self.user['apikey'], 'sha1')
|
|
159
|
+
token = dict(**token, **dict(user=self.user['name'], token=common.encrypt(path, hashed)))
|
|
160
|
+
token = convert.str2b64str(common.to_str(token))
|
|
161
|
+
webbrowser.open(f"{self.endpoint}/dosignin_token/{token}{path}")
|
|
162
|
+
return 0, dict(success="Open browser.")
|
|
163
|
+
elif self.user['auth_type'] == "oauth2":
|
|
164
|
+
if self.oauth2 == 'google':
|
|
165
|
+
webbrowser.open(f"{self.endpoint}/oauth2/google/session/{self.user['access_token']}{path}")
|
|
166
|
+
return 0, dict(success="Open browser.")
|
|
167
|
+
if self.oauth2 == 'github':
|
|
168
|
+
webbrowser.open(f"{self.endpoint}/oauth2/github/session/{self.user['access_token']}{path}")
|
|
169
|
+
return 0, dict(success="Open browser.")
|
|
170
|
+
if self.oauth2 == 'azure':
|
|
171
|
+
webbrowser.open(f"{self.endpoint}/oauth2/azure/session/{self.user['access_token']}{path}")
|
|
172
|
+
return 0, dict(success="Open browser.")
|
|
173
|
+
elif self.user['auth_type'] == "saml":
|
|
174
|
+
if self.saml == 'azure':
|
|
175
|
+
webbrowser.open(f"{self.endpoint}/saml/azure/session/{self.user['saml_token']}{path}")
|
|
176
|
+
return 0, dict(success="Open browser.")
|
|
177
|
+
return 1, dict(warn="unsupported auth_type.")
|
cmdbox/app/feature.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from cmdbox import version
|
|
2
|
-
from cmdbox.app import
|
|
3
|
-
from cmdbox.app.commons import redis_client
|
|
2
|
+
from cmdbox.app import common, edge_tool
|
|
3
|
+
from cmdbox.app.commons import convert, redis_client
|
|
4
4
|
from cmdbox.app.web import Web
|
|
5
5
|
from fastapi import FastAPI
|
|
6
6
|
from pathlib import Path
|
|
@@ -27,6 +27,7 @@ class Feature(object):
|
|
|
27
27
|
self.ver = ver
|
|
28
28
|
self.appcls = appcls
|
|
29
29
|
self.default_svname:str = ver.__appid__
|
|
30
|
+
self.default_data:Path = os.environ.get('DATA_DIR', common.HOME_DIR / f".{self.ver.__appid__}")
|
|
30
31
|
|
|
31
32
|
def get_mode(self) -> Union[str, List[str]]:
|
|
32
33
|
"""
|
|
@@ -105,13 +106,13 @@ class Feature(object):
|
|
|
105
106
|
"""
|
|
106
107
|
raise NotImplementedError
|
|
107
108
|
|
|
108
|
-
def edgerun(self, opt:Dict[str, Any], tool:
|
|
109
|
+
def edgerun(self, opt:Dict[str, Any], tool:edge_tool.Tool, logger:logging.Logger, timeout:int, prevres:Any=None):
|
|
109
110
|
"""
|
|
110
111
|
この機能のエッジ側の実行を行います
|
|
111
112
|
|
|
112
113
|
Args:
|
|
113
114
|
opt (Dict[str, Any]): オプション
|
|
114
|
-
tool (
|
|
115
|
+
tool (edge_tool.Tool): 通知関数などedge側のUI操作を行うためのクラス
|
|
115
116
|
logger (logging.Logger): ロガー
|
|
116
117
|
timeout (int): タイムアウト時間
|
|
117
118
|
prevres (Any): 前コマンドの結果。pipeline実行の実行結果を参照する時に使用します。
|
|
@@ -126,7 +127,7 @@ class OneshotEdgeFeature(Feature):
|
|
|
126
127
|
"""
|
|
127
128
|
一度だけ実行するエッジ機能の基底クラス
|
|
128
129
|
"""
|
|
129
|
-
def edgerun(self, opt:Dict[str, Any], tool:
|
|
130
|
+
def edgerun(self, opt:Dict[str, Any], tool:edge_tool.Tool, logger:logging.Logger, timeout:int, prevres:Any=None):
|
|
130
131
|
status, res = tool.exec_cmd(opt, logger, timeout, prevres)
|
|
131
132
|
yield 1, res
|
|
132
133
|
|
|
@@ -134,7 +135,7 @@ class OneshotNotifyEdgeFeature(OneshotEdgeFeature):
|
|
|
134
135
|
"""
|
|
135
136
|
実行結果の通知を行うエッジ機能の基底クラス
|
|
136
137
|
"""
|
|
137
|
-
def edgerun(self, opt:Dict[str, Any], tool:
|
|
138
|
+
def edgerun(self, opt:Dict[str, Any], tool:edge_tool.Tool, logger:logging.Logger, timeout:int, prevres:Any=None):
|
|
138
139
|
status, res = next(super().edgerun(opt, tool, logger, timeout, prevres))
|
|
139
140
|
tool.notify(res)
|
|
140
141
|
yield status, res
|
|
@@ -143,7 +144,7 @@ class ResultEdgeFeature(Feature):
|
|
|
143
144
|
"""
|
|
144
145
|
実行結果をWebブラウザで表示するエッジ機能の基底クラス
|
|
145
146
|
"""
|
|
146
|
-
def edgerun(self, opt:Dict[str, Any], tool:
|
|
147
|
+
def edgerun(self, opt:Dict[str, Any], tool:edge_tool.Tool, logger:logging.Logger, timeout:int, prevres:Any=None):
|
|
147
148
|
status, res = next(super().edgerun(opt, tool, logger, timeout, prevres))
|
|
148
149
|
if status == 0:
|
|
149
150
|
status, res = tool.pub_result(opt['title'], res, timeout)
|
|
@@ -155,7 +156,7 @@ class OneshotResultEdgeFeature(ResultEdgeFeature):
|
|
|
155
156
|
"""
|
|
156
157
|
一度だけ実行結果をWebブラウザで表示するエッジ機能の基底クラス
|
|
157
158
|
"""
|
|
158
|
-
def edgerun(self, opt:Dict[str, Any], tool:
|
|
159
|
+
def edgerun(self, opt:Dict[str, Any], tool:edge_tool.Tool, logger:logging.Logger, timeout:int, prevres:Any=None):
|
|
159
160
|
status, res = next(super().edgerun(opt, tool, logger, timeout, prevres))
|
|
160
161
|
yield 1, res
|
|
161
162
|
|
|
@@ -163,7 +164,7 @@ class UnsupportEdgeFeature(Feature):
|
|
|
163
164
|
"""
|
|
164
165
|
サポートされていないエッジ機能の基底クラス
|
|
165
166
|
"""
|
|
166
|
-
def edgerun(self, opt:Dict[str, Any], tool:
|
|
167
|
+
def edgerun(self, opt:Dict[str, Any], tool:edge_tool.Tool, logger:logging.Logger, timeout:int, prevres:Any=None):
|
|
167
168
|
res = dict(warn=f'Unsupported edgerun. mode="{opt["mode"]}", cmd="{opt["cmd"]}"')
|
|
168
169
|
tool.notify(res)
|
|
169
170
|
yield 1, res
|