cmdbox 0.5.2__py3-none-any.whl → 0.5.3.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 +1 -0
- cmdbox/app/feature.py +2 -0
- cmdbox/app/features/cli/audit_base.py +5 -2
- cmdbox/app/features/cli/cmdbox_audit_createdb.py +1 -1
- cmdbox/app/features/cli/cmdbox_audit_delete.py +27 -18
- cmdbox/app/features/cli/cmdbox_audit_search.py +128 -62
- cmdbox/app/features/cli/cmdbox_audit_write.py +31 -20
- cmdbox/app/features/cli/cmdbox_client_file_copy.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_file_download.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_file_list.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_file_mkdir.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_file_move.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_file_remove.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_file_rmdir.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_file_upload.py +1 -1
- cmdbox/app/features/cli/cmdbox_client_server_info.py +1 -1
- cmdbox/app/features/cli/cmdbox_gui_start.py +1 -1
- cmdbox/app/features/cli/cmdbox_server_start.py +1 -1
- cmdbox/app/features/cli/cmdbox_server_stop.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_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 +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_audit.py +87 -0
- cmdbox/app/features/web/cmdbox_web_audit_metrics.py +72 -0
- cmdbox/app/features/web/cmdbox_web_exec_cmd.py +10 -5
- cmdbox/app/features/web/cmdbox_web_user_data.py +58 -0
- cmdbox/app/options.py +49 -19
- cmdbox/app/server.py +15 -3
- cmdbox/app/web.py +7 -1
- cmdbox/extensions/features.yml +11 -8
- cmdbox/extensions/sample_project/sample/app/features/cli/sample_server_time.py +1 -1
- cmdbox/extensions/user_list.yml +5 -0
- cmdbox/licenses/LICENSE.Jinja2.3.1.4(BSD License).txt +28 -0
- cmdbox/licenses/LICENSE.Pygments.2.18.0(BSD License).txt +25 -0
- cmdbox/licenses/LICENSE.Sphinx.8.1.3(BSD License).txt +31 -0
- cmdbox/licenses/LICENSE.anyio.4.6.2.post1(MIT License).txt +20 -0
- cmdbox/licenses/LICENSE.argcomplete.3.5.1(Apache Software License).txt +177 -0
- cmdbox/licenses/LICENSE.argcomplete.3.6.2(Apache Software License).txt +177 -0
- cmdbox/licenses/LICENSE.babel.2.16.0(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.certifi.2024.8.30(Mozilla Public License 2.0 (MPL 2.0)).txt +20 -0
- cmdbox/licenses/LICENSE.charset-normalizer.3.4.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.click.8.1.7(BSD License).txt +28 -0
- cmdbox/licenses/LICENSE.cryptography.43.0.3(Apache Software License; BSD License).txt +3 -0
- cmdbox/licenses/LICENSE.fastapi.0.115.5(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.gevent.25.4.1(MIT).txt +25 -0
- cmdbox/licenses/LICENSE.greenlet.3.2.0(MIT AND Python-2.0).txt +30 -0
- cmdbox/licenses/LICENSE.importlib_metadata.8.5.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.keyring.25.5.0(MIT License).txt +17 -0
- cmdbox/licenses/LICENSE.more-itertools.10.5.0(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.nh3.0.2.18(MIT).txt +1 -0
- cmdbox/licenses/LICENSE.numpy.2.1.3(BSD License).txt +950 -0
- cmdbox/licenses/LICENSE.pillow.11.0.0(CMU License (MIT-CMU)).txt +1226 -0
- cmdbox/licenses/LICENSE.pillow.11.2.1(UNKNOWN).txt +1200 -0
- cmdbox/licenses/LICENSE.pkginfo.1.10.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.prettytable.3.12.0(BSD License).txt +30 -0
- cmdbox/licenses/LICENSE.prompt_toolkit.3.0.51(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.pydantic.2.10.2(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.pydantic.2.11.3(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.pydantic_core.2.27.1(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.pydantic_core.2.33.1(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.python-dotenv.1.0.1(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.python-multipart.0.0.17(Apache Software License).txt +14 -0
- cmdbox/licenses/LICENSE.redis.5.2.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.rich.13.9.4(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.six.1.16.0(MIT License).txt +18 -0
- cmdbox/licenses/LICENSE.sphinx-intl.2.3.0(BSD License).txt +25 -0
- cmdbox/licenses/LICENSE.starlette.0.41.3(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.starlette.0.46.2(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.tomli.2.1.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.twine.5.1.1(Apache Software License).txt +174 -0
- cmdbox/licenses/LICENSE.typing_extensions.4.12.2(Python Software Foundation License).txt +279 -0
- cmdbox/licenses/LICENSE.typing_extensions.4.13.2(UNKNOWN).txt +279 -0
- cmdbox/licenses/LICENSE.urllib3.2.2.3(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.urllib3.2.4.0(UNKNOWN).txt +21 -0
- cmdbox/licenses/LICENSE.uvicorn.0.32.1(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.uvicorn.0.34.1(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.watchfiles.1.0.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.watchfiles.1.0.5(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.websockets.14.1(BSD License).txt +24 -0
- cmdbox/licenses/LICENSE.zope.interface.7.1.1(Zope Public License).txt +44 -0
- cmdbox/licenses/files.txt +12 -13
- cmdbox/version.py +2 -2
- cmdbox/web/assets/apexcharts/apexcharts.css +679 -0
- cmdbox/web/assets/apexcharts/apexcharts.min.js +38 -0
- cmdbox/web/assets/cmdbox/audit.js +404 -0
- cmdbox/web/assets/cmdbox/color_mode.css +4 -0
- cmdbox/web/assets/cmdbox/common.js +397 -24
- cmdbox/web/assets/cmdbox/filer_modal.js +1 -1
- cmdbox/web/assets/cmdbox/list_cmd.js +7 -271
- cmdbox/web/assets/cmdbox/list_pipe.js +3 -3
- cmdbox/web/assets/cmdbox/users.js +17 -17
- cmdbox/web/assets/cmdbox/view_raw.js +1 -1
- cmdbox/web/assets/cmdbox/view_result.js +11 -13
- cmdbox/web/assets/filer/filer.js +2 -2
- cmdbox/web/assets_license_list.txt +4 -1
- cmdbox/web/audit.html +293 -0
- cmdbox/web/filer.html +21 -10
- cmdbox/web/gui.html +21 -52
- cmdbox/web/result.html +9 -2
- cmdbox/web/users.html +7 -3
- {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.1.dist-info}/METADATA +13 -10
- {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.1.dist-info}/RECORD +114 -59
- {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.1.dist-info}/LICENSE +0 -0
- {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.1.dist-info}/WHEEL +0 -0
- {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.1.dist-info}/entry_points.txt +0 -0
- {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.1.dist-info}/top_level.txt +0 -0
|
@@ -47,7 +47,7 @@ class ClientFileList(feature.OneshotResultEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="svpath", type=Options.T_STR, default="/", required=True, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class ClientFileMkdir(feature.UnsupportEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="svpath", type=Options.T_STR, default="/", required=True, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class ClientFileMove(feature.UnsupportEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="from_path", type=Options.T_STR, default=None, required=True, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class ClientFileRemove(feature.UnsupportEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="svpath", type=Options.T_STR, default="/", required=True, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class ClientFileRmdir(feature.UnsupportEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="svpath", type=Options.T_STR, default="/", required=True, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class ClientFileUpload(feature.UnsupportEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="svpath", type=Options.T_STR, default="/", required=True, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class ClientServerInfo(feature.OneshotResultEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="retry_count", type=Options.T_INT, default=3, required=False, multi=False, hide=True, choice=None,
|
|
@@ -47,7 +47,7 @@ class GuiStart(feature.UnsupportEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class ServerStart(feature.OneshotNotifyEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class ServerStop(feature.OneshotNotifyEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="retry_count", type=Options.T_INT, default=3, required=False, multi=False, hide=True, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebApikeyAdd(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebApikeyDel(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebGroupAdd(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebGroupDel(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebGroupEdit(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebGroupList(feature.OneshotResultEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -47,7 +47,7 @@ class WebStart(feature.UnsupportEdgeFeature):
|
|
|
47
47
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
48
48
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
49
49
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
50
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
50
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
51
51
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
52
52
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
53
53
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebUserAdd(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebUserDel(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebUserEdit(feature.UnsupportEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -46,7 +46,7 @@ class WebUserList(feature.OneshotResultEdgeFeature):
|
|
|
46
46
|
dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
|
|
47
47
|
discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
|
|
48
48
|
discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
|
|
49
|
-
dict(opt="svname", type=Options.T_STR, default=
|
|
49
|
+
dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
|
|
50
50
|
discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
|
|
51
51
|
discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
|
|
52
52
|
dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from cmdbox.app import common, feature
|
|
2
|
+
from cmdbox.app.web import Web
|
|
3
|
+
from fastapi import FastAPI, Request, Response
|
|
4
|
+
from fastapi.responses import HTMLResponse
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
import argparse
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Audit(feature.WebFeature):
|
|
11
|
+
|
|
12
|
+
def route(self, web:Web, app:FastAPI) -> None:
|
|
13
|
+
"""
|
|
14
|
+
webモードのルーティングを設定します
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
web (Web): Webオブジェクト
|
|
18
|
+
app (FastAPI): FastAPIオブジェクト
|
|
19
|
+
"""
|
|
20
|
+
if web.audit_html is not None:
|
|
21
|
+
if not web.audit_html.is_file():
|
|
22
|
+
raise FileNotFoundError(f'audit_html is not found. ({web.audit_html})')
|
|
23
|
+
with open(web.audit_html, 'r', encoding='utf-8') as f:
|
|
24
|
+
web.audit_html_data = f.read()
|
|
25
|
+
|
|
26
|
+
@app.get('/audit', response_class=HTMLResponse)
|
|
27
|
+
@app.post('/audit', response_class=HTMLResponse)
|
|
28
|
+
async def audit(req:Request, res:Response):
|
|
29
|
+
signin = web.signin.check_signin(req, res)
|
|
30
|
+
if signin is not None:
|
|
31
|
+
return signin
|
|
32
|
+
res.headers['Access-Control-Allow-Origin'] = '*'
|
|
33
|
+
web.options.audit_exec(req, res, web)
|
|
34
|
+
return web.audit_html_data
|
|
35
|
+
|
|
36
|
+
@app.post('/audit/rawlog')
|
|
37
|
+
async def audit_rawlog(req:Request, res:Response):
|
|
38
|
+
signin = web.signin.check_signin(req, res)
|
|
39
|
+
if signin is not None:
|
|
40
|
+
return signin
|
|
41
|
+
if web.signin.get_data() is None:
|
|
42
|
+
return dict(error='signin_file_data is None.')
|
|
43
|
+
if not hasattr(web.options, 'audit_search') or web.options.audit_search is None:
|
|
44
|
+
return dict(warn='audit feature is disabled.')
|
|
45
|
+
opt = await req.json()
|
|
46
|
+
opt = {**opt, **web.options.audit_search_args.copy()}
|
|
47
|
+
opt['host'] = web.redis_host
|
|
48
|
+
opt['port'] = web.redis_port
|
|
49
|
+
opt['password'] = web.redis_password
|
|
50
|
+
opt['svname'] = web.svname
|
|
51
|
+
args = argparse.Namespace(**{k:common.chopdq(v) for k,v in opt.items()})
|
|
52
|
+
status, ret_main, _ = web.options.audit_search.apprun(web.logger, args, time.perf_counter(), [])
|
|
53
|
+
if status != 0:
|
|
54
|
+
return dict(error=ret_main)
|
|
55
|
+
return ret_main
|
|
56
|
+
|
|
57
|
+
@app.get('/audit/mode_cmd')
|
|
58
|
+
async def audit_mode_cmd(req:Request, res:Response):
|
|
59
|
+
signin = web.signin.check_signin(req, res)
|
|
60
|
+
if signin is not None:
|
|
61
|
+
return signin
|
|
62
|
+
if not hasattr(web.options, 'audit_search_args'):
|
|
63
|
+
return dict(warn='audit feature is disabled.')
|
|
64
|
+
return dict(success=web.options.audit_search_args)
|
|
65
|
+
|
|
66
|
+
def toolmenu(self, web:Web) -> Dict[str, Any]:
|
|
67
|
+
"""
|
|
68
|
+
ツールメニューの情報を返します
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
web (Web): Webオブジェクト
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Dict[str, Any]: ツールメニュー情報
|
|
75
|
+
|
|
76
|
+
Sample:
|
|
77
|
+
{
|
|
78
|
+
'filer': {
|
|
79
|
+
'html': 'Filer',
|
|
80
|
+
'href': 'filer',
|
|
81
|
+
'target': '_blank',
|
|
82
|
+
'css_class': 'dropdown-item'
|
|
83
|
+
'onclick': 'alert("filer")'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
"""
|
|
87
|
+
return dict(audit=dict(html='Audit', href='audit', target='_blank', css_class='dropdown-item'))
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from cmdbox.app import common, feature
|
|
2
|
+
from cmdbox.app.web import Web
|
|
3
|
+
from fastapi import FastAPI, Request, Response, HTTPException
|
|
4
|
+
from typing import Dict, Any
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AuditMetrics(feature.WebFeature):
|
|
9
|
+
def route(self, web:Web, app:FastAPI) -> None:
|
|
10
|
+
"""
|
|
11
|
+
webモードのルーティングを設定します
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
web (Web): Webオブジェクト
|
|
15
|
+
app (FastAPI): FastAPIオブジェクト
|
|
16
|
+
"""
|
|
17
|
+
@app.post('/audit/metrics/save')
|
|
18
|
+
async def save_metrics(req:Request, res:Response):
|
|
19
|
+
signin = web.signin.check_signin(req, res)
|
|
20
|
+
if signin is not None:
|
|
21
|
+
raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
|
|
22
|
+
form = await req.form()
|
|
23
|
+
title = form.get('title')
|
|
24
|
+
opt = json.loads(form.get('opt'))
|
|
25
|
+
if common.check_fname(title):
|
|
26
|
+
return dict(warn=f'The title contains invalid characters."{title}"')
|
|
27
|
+
opt_path = web.audit_path / f"metrics-{title}.json"
|
|
28
|
+
web.logger.info(f"save_metrics: opt_path={opt_path}, opt={opt}")
|
|
29
|
+
common.saveopt(opt, opt_path, True)
|
|
30
|
+
ret = dict(success=f'Metrics "{title}" saved in "{opt_path}".')
|
|
31
|
+
web.options.audit_exec(req, res, web, title=title)
|
|
32
|
+
return ret
|
|
33
|
+
|
|
34
|
+
@app.post('/audit/metrics/load')
|
|
35
|
+
async def load_metrics(req:Request, res:Response):
|
|
36
|
+
signin = web.signin.check_signin(req, res)
|
|
37
|
+
if signin is not None:
|
|
38
|
+
raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
|
|
39
|
+
form = await req.form()
|
|
40
|
+
title = form.get('title')
|
|
41
|
+
opt_path = web.audit_path / f"metrics-{title}.json"
|
|
42
|
+
if not opt_path.is_file():
|
|
43
|
+
return dict(warn=f'The metrics file is not found."{opt_path}"')
|
|
44
|
+
with open(opt_path, 'r', encoding='utf-8') as f:
|
|
45
|
+
opt = json.load(f)
|
|
46
|
+
return dict(success=opt)
|
|
47
|
+
|
|
48
|
+
@app.post('/audit/metrics/delete')
|
|
49
|
+
async def delete_metrics(req:Request, res:Response):
|
|
50
|
+
signin = web.signin.check_signin(req, res)
|
|
51
|
+
if signin is not None:
|
|
52
|
+
raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
|
|
53
|
+
form = await req.form()
|
|
54
|
+
title = form.get('title')
|
|
55
|
+
opt_path = web.audit_path / f"metrics-{title}.json"
|
|
56
|
+
if not opt_path.is_file():
|
|
57
|
+
return dict(warn=f'The metrics file is not found."{opt_path}"')
|
|
58
|
+
opt_path.unlink()
|
|
59
|
+
return dict(success=f'Metrics "{title}" deleted.')
|
|
60
|
+
|
|
61
|
+
@app.post('/audit/metrics/list')
|
|
62
|
+
async def list_metrics(req:Request, res:Response):
|
|
63
|
+
signin = web.signin.check_signin(req, res)
|
|
64
|
+
if signin is not None:
|
|
65
|
+
raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
|
|
66
|
+
files = web.audit_path.glob('metrics-*.json')
|
|
67
|
+
ret = []
|
|
68
|
+
for f in files:
|
|
69
|
+
with open(f, 'r', encoding='utf-8') as f:
|
|
70
|
+
opt = json.load(f)
|
|
71
|
+
ret.append(opt)
|
|
72
|
+
return dict(success=ret)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from cmdbox.app import app, client, common, options, server, web as _web
|
|
2
2
|
from cmdbox.app.commons import convert, loghandler
|
|
3
|
-
from cmdbox.app.features.cli import
|
|
3
|
+
from cmdbox.app.features.cli import cmdbox_audit_search, cmdbox_audit_write
|
|
4
4
|
from cmdbox.app.features.web import cmdbox_web_load_cmd
|
|
5
5
|
from cmdbox.app.web import Web
|
|
6
6
|
from fastapi import FastAPI, Request, Response, HTTPException
|
|
@@ -96,7 +96,6 @@ class ExecCmd(cmdbox_web_load_cmd.LoadCmd):
|
|
|
96
96
|
return True, output
|
|
97
97
|
return False, None
|
|
98
98
|
|
|
99
|
-
@options.Options.audit()
|
|
100
99
|
def exec_cmd(self, req:Request, res:Response, web:Web,
|
|
101
100
|
title:str, opt:Dict[str, Any], nothread:bool=False, appcls=None) -> List[Dict[str, Any]]:
|
|
102
101
|
"""
|
|
@@ -113,6 +112,10 @@ class ExecCmd(cmdbox_web_load_cmd.LoadCmd):
|
|
|
113
112
|
Returns:
|
|
114
113
|
list: コマンド実行結果
|
|
115
114
|
"""
|
|
115
|
+
tags = []
|
|
116
|
+
if 'tag' in opt and isinstance(opt['tag'], list):
|
|
117
|
+
tags = [t for t in opt['tag'] if t is not None and t != '']
|
|
118
|
+
web.options.audit_exec(req, res, web, tags=tags, title=title)
|
|
116
119
|
appcls = self.appcls if appcls is None else appcls
|
|
117
120
|
appcls = app.CmdBoxApp if appcls is None else appcls
|
|
118
121
|
web.container['cmdbox_app'] = ap = appcls.getInstance(appcls=appcls, ver=self.ver)
|
|
@@ -133,8 +136,10 @@ class ExecCmd(cmdbox_web_load_cmd.LoadCmd):
|
|
|
133
136
|
found = True
|
|
134
137
|
if not found or o not in loaded: continue
|
|
135
138
|
opt[o] = loaded[o]
|
|
136
|
-
if isinstance(feat,
|
|
137
|
-
opt[o] = _options.
|
|
139
|
+
if isinstance(feat, cmdbox_audit_write.AuditWrite) and hasattr(_options, 'audit_write_args') and o in _options.audit_write_args:
|
|
140
|
+
opt[o] = _options.audit_write_args[o]
|
|
141
|
+
elif isinstance(feat, cmdbox_audit_search.AuditSearch) and hasattr(_options, 'audit_search_args') and o in _options.audit_search_args:
|
|
142
|
+
opt[o] = _options.audit_search_args[o]
|
|
138
143
|
except:
|
|
139
144
|
pass
|
|
140
145
|
if 'host' in opt: opt['host'] = web.redis_host
|
|
@@ -175,7 +180,7 @@ class ExecCmd(cmdbox_web_load_cmd.LoadCmd):
|
|
|
175
180
|
logsize = 1024
|
|
176
181
|
try:
|
|
177
182
|
old_stdout.write(loghandler.colorize_msg(f'EXEC: {opt_list}\n'[:logsize]))
|
|
178
|
-
status, ret_main, obj = cmdbox_app.main(args_list=opt_list, file_dict=file_dict, webcall=True)
|
|
183
|
+
status, ret_main, obj = cmdbox_app.main(args_list=[common.chopdq(o) for o in opt_list], file_dict=file_dict, webcall=True)
|
|
179
184
|
if isinstance(obj, server.Server):
|
|
180
185
|
cmdbox_app.sv = obj
|
|
181
186
|
elif isinstance(obj, client.Client):
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from cmdbox.app import common, feature
|
|
2
|
+
from cmdbox.app.web import Web
|
|
3
|
+
from fastapi import FastAPI, Request, Response, HTTPException
|
|
4
|
+
from typing import Dict, Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class UserData(feature.WebFeature):
|
|
8
|
+
def route(self, web:Web, app:FastAPI) -> None:
|
|
9
|
+
"""
|
|
10
|
+
webモードのルーティングを設定します
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
web (Web): Webオブジェクト
|
|
14
|
+
app (FastAPI): FastAPIオブジェクト
|
|
15
|
+
"""
|
|
16
|
+
@app.post('/gui/user_data/load')
|
|
17
|
+
async def load(req:Request, res:Response):
|
|
18
|
+
signin = web.signin.check_signin(req, res)
|
|
19
|
+
if signin is not None:
|
|
20
|
+
raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
|
|
21
|
+
if 'signin' not in req.session or req.session['signin'] is None:
|
|
22
|
+
return dict(warn='Please sign in.')
|
|
23
|
+
form = await req.form()
|
|
24
|
+
categoly = form.get('categoly')
|
|
25
|
+
key = form.get('key')
|
|
26
|
+
sess = req.session['signin']
|
|
27
|
+
ret = web.user_data(req, sess['uid'], sess['name'], categoly, key)
|
|
28
|
+
return dict(success=ret)
|
|
29
|
+
|
|
30
|
+
@app.post('/gui/user_data/save')
|
|
31
|
+
async def save(req:Request, res:Response):
|
|
32
|
+
signin = web.signin.check_signin(req, res)
|
|
33
|
+
if signin is not None:
|
|
34
|
+
raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
|
|
35
|
+
if 'signin' not in req.session or req.session['signin'] is None:
|
|
36
|
+
return dict(warn='Please sign in.')
|
|
37
|
+
form = await req.form()
|
|
38
|
+
categoly = form.get('categoly')
|
|
39
|
+
key = form.get('key')
|
|
40
|
+
val = form.get('val')
|
|
41
|
+
sess = req.session['signin']
|
|
42
|
+
web.user_data(req, sess['uid'], sess['name'], categoly, key, val)
|
|
43
|
+
return dict(success=f'user_data "{categoly}:{key}:val" saved.')
|
|
44
|
+
|
|
45
|
+
@app.post('/gui/user_data/delete')
|
|
46
|
+
async def delete(req:Request, res:Response):
|
|
47
|
+
signin = web.signin.check_signin(req, res)
|
|
48
|
+
if signin is not None:
|
|
49
|
+
raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
|
|
50
|
+
if 'signin' not in req.session or req.session['signin'] is None:
|
|
51
|
+
return dict(warn='Please sign in.')
|
|
52
|
+
form = await req.form()
|
|
53
|
+
categoly = form.get('categoly')
|
|
54
|
+
key = form.get('key')
|
|
55
|
+
val = form.get('val')
|
|
56
|
+
sess = req.session['signin']
|
|
57
|
+
web.user_data(req, sess['uid'], sess['name'], categoly, key, delkey=True)
|
|
58
|
+
return dict(success=f'user_data "{categoly}:{key}:val" deleted.')
|