cmdbox 0.5.1.2__py3-none-any.whl → 0.5.3__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.

Files changed (143) hide show
  1. cmdbox/app/app.py +4 -2
  2. cmdbox/app/auth/signin.py +634 -631
  3. cmdbox/app/client.py +10 -10
  4. cmdbox/app/common.py +50 -6
  5. cmdbox/app/commons/convert.py +9 -0
  6. cmdbox/app/commons/module.py +113 -113
  7. cmdbox/app/commons/redis_client.py +40 -29
  8. cmdbox/app/edge.py +4 -4
  9. cmdbox/app/features/cli/audit_base.py +138 -0
  10. cmdbox/app/features/cli/cmdbox_audit_createdb.py +224 -0
  11. cmdbox/app/features/cli/cmdbox_audit_delete.py +308 -0
  12. cmdbox/app/features/cli/cmdbox_audit_search.py +416 -0
  13. cmdbox/app/features/cli/cmdbox_audit_write.py +247 -0
  14. cmdbox/app/features/cli/cmdbox_client_file_copy.py +207 -207
  15. cmdbox/app/features/cli/cmdbox_client_file_download.py +207 -207
  16. cmdbox/app/features/cli/cmdbox_client_file_list.py +193 -193
  17. cmdbox/app/features/cli/cmdbox_client_file_mkdir.py +191 -191
  18. cmdbox/app/features/cli/cmdbox_client_file_move.py +199 -199
  19. cmdbox/app/features/cli/cmdbox_client_file_remove.py +190 -190
  20. cmdbox/app/features/cli/cmdbox_client_file_rmdir.py +190 -190
  21. cmdbox/app/features/cli/cmdbox_client_file_upload.py +212 -212
  22. cmdbox/app/features/cli/cmdbox_client_server_info.py +166 -166
  23. cmdbox/app/features/cli/cmdbox_server_list.py +88 -88
  24. cmdbox/app/features/cli/cmdbox_server_stop.py +138 -138
  25. cmdbox/app/features/web/cmdbox_web_audit.py +81 -0
  26. cmdbox/app/features/web/cmdbox_web_audit_metrics.py +72 -0
  27. cmdbox/app/features/web/cmdbox_web_del_cmd.py +2 -0
  28. cmdbox/app/features/web/cmdbox_web_del_pipe.py +1 -0
  29. cmdbox/app/features/web/cmdbox_web_do_signin.py +12 -2
  30. cmdbox/app/features/web/cmdbox_web_do_signout.py +1 -0
  31. cmdbox/app/features/web/cmdbox_web_exec_cmd.py +31 -2
  32. cmdbox/app/features/web/cmdbox_web_exec_pipe.py +1 -0
  33. cmdbox/app/features/web/cmdbox_web_filer download.py +43 -42
  34. cmdbox/app/features/web/cmdbox_web_filer.py +1 -0
  35. cmdbox/app/features/web/cmdbox_web_filer_upload.py +65 -64
  36. cmdbox/app/features/web/cmdbox_web_gui.py +166 -165
  37. cmdbox/app/features/web/cmdbox_web_load_pin.py +43 -43
  38. cmdbox/app/features/web/cmdbox_web_raw_pipe.py +87 -87
  39. cmdbox/app/features/web/cmdbox_web_save_cmd.py +1 -0
  40. cmdbox/app/features/web/cmdbox_web_save_pin.py +42 -42
  41. cmdbox/app/features/web/cmdbox_web_save_pipe.py +1 -0
  42. cmdbox/app/features/web/cmdbox_web_user_data.py +58 -0
  43. cmdbox/app/features/web/cmdbox_web_users.py +12 -0
  44. cmdbox/app/options.py +788 -601
  45. cmdbox/app/web.py +7 -1
  46. cmdbox/extensions/features.yml +23 -0
  47. cmdbox/extensions/sample_project/sample/app/features/cli/sample_client_time.py +82 -82
  48. cmdbox/extensions/sample_project/sample/app/features/cli/sample_server_time.py +145 -145
  49. cmdbox/extensions/user_list.yml +5 -0
  50. cmdbox/licenses/{LICENSE.Sphinx.8.1.3(BSD License).txt → LICENSE.Sphinx.8.2.3(UNKNOWN).txt} +1 -1
  51. cmdbox/licenses/LICENSE.argcomplete.3.6.2(Apache Software License).txt +177 -0
  52. cmdbox/licenses/{LICENSE.babel.2.16.0(BSD License).txt → LICENSE.babel.2.17.0(BSD License).txt } +1 -1
  53. cmdbox/licenses/{LICENSE.pkginfo.1.10.0(MIT License).txt → LICENSE.charset-normalizer.3.4.1(MIT License).txt } +1 -1
  54. cmdbox/licenses/LICENSE.gevent.25.4.1(MIT).txt +25 -0
  55. cmdbox/licenses/LICENSE.greenlet.3.2.0(MIT AND Python-2.0).txt +30 -0
  56. cmdbox/licenses/LICENSE.gunicorn.23.0.0(MIT License).txt +23 -0
  57. cmdbox/licenses/LICENSE.importlib_metadata.8.6.1(Apache Software License).txt +202 -0
  58. cmdbox/licenses/LICENSE.nh3.0.2.21(MIT).txt +21 -0
  59. cmdbox/licenses/{LICENSE.pillow.11.0.0(CMU License (MIT-CMU)).txt → LICENSE.pillow.11.1.0(CMU License (MIT-CMU)).txt } +27 -40
  60. cmdbox/licenses/LICENSE.pillow.11.2.1(UNKNOWN).txt +1200 -0
  61. cmdbox/licenses/LICENSE.plyer.2.1.0(MIT License).txt +19 -0
  62. cmdbox/licenses/LICENSE.prompt_toolkit.3.0.50(BSD License).txt +27 -0
  63. cmdbox/licenses/LICENSE.prompt_toolkit.3.0.51(BSD License).txt +27 -0
  64. cmdbox/licenses/LICENSE.psycopg-binary.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt +165 -0
  65. cmdbox/licenses/LICENSE.psycopg-pool.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt +165 -0
  66. cmdbox/licenses/LICENSE.psycopg.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt +165 -0
  67. cmdbox/licenses/LICENSE.pycryptodome.3.22.0(BSD License; Public Domain).txt +61 -0
  68. cmdbox/licenses/LICENSE.pydantic.2.11.3(MIT License).txt +21 -0
  69. cmdbox/licenses/LICENSE.pydantic_core.2.33.1(MIT License).txt +21 -0
  70. cmdbox/licenses/LICENSE.pystray.0.19.5(GNU Lesser General Public License v3 (LGPLv3)).txt +674 -0
  71. cmdbox/licenses/LICENSE.questionary.2.1.0(MIT License).txt +19 -0
  72. cmdbox/licenses/LICENSE.roman-numerals-py.3.1.0(CC0 1.0 Universal (CC0 1.0) Public Domain Dedication; Zero-Clause BSD (0BSD)).txt +146 -0
  73. cmdbox/licenses/{LICENSE.six.1.16.0(MIT License).txt → LICENSE.six.1.17.0(MIT License).txt } +1 -1
  74. cmdbox/licenses/LICENSE.starlette.0.46.2(BSD License).txt +27 -0
  75. cmdbox/licenses/{LICENSE.charset-normalizer.3.4.0(MIT License).txt → LICENSE.typing-inspection.0.4.0(MIT License).txt } +2 -2
  76. cmdbox/licenses/LICENSE.typing_extensions.4.13.2(UNKNOWN).txt +279 -0
  77. cmdbox/licenses/LICENSE.tzdata.2025.2(Apache Software License).txt +15 -0
  78. cmdbox/licenses/LICENSE.urllib3.2.4.0(UNKNOWN).txt +21 -0
  79. cmdbox/licenses/LICENSE.uvicorn.0.34.1(BSD License).txt +27 -0
  80. cmdbox/licenses/LICENSE.watchfiles.1.0.5(MIT License).txt +21 -0
  81. cmdbox/licenses/files.txt +49 -38
  82. cmdbox/logconf_audit.yml +30 -0
  83. cmdbox/logconf_cmdbox.yml +30 -0
  84. cmdbox/version.py +2 -2
  85. cmdbox/web/assets/apexcharts/apexcharts.css +679 -0
  86. cmdbox/web/assets/apexcharts/apexcharts.min.js +38 -0
  87. cmdbox/web/assets/cmdbox/audit.js +340 -0
  88. cmdbox/web/assets/cmdbox/color_mode.css +520 -0
  89. cmdbox/web/assets/cmdbox/common.js +416 -24
  90. cmdbox/web/assets/cmdbox/filer_modal.js +1 -1
  91. cmdbox/web/assets/cmdbox/list_cmd.js +10 -275
  92. cmdbox/web/assets/cmdbox/list_pipe.js +3 -3
  93. cmdbox/web/assets/cmdbox/main.js +2 -2
  94. cmdbox/web/assets/cmdbox/result.js +2 -2
  95. cmdbox/web/assets/cmdbox/signin.js +2 -2
  96. cmdbox/web/assets/cmdbox/users.js +19 -20
  97. cmdbox/web/assets/cmdbox/view_raw.js +1 -1
  98. cmdbox/web/assets/cmdbox/view_result.js +11 -13
  99. cmdbox/web/assets/filer/filer.js +2 -2
  100. cmdbox/web/assets/filer/main.js +2 -2
  101. cmdbox/web/assets_license_list.txt +4 -1
  102. cmdbox/web/audit.html +268 -0
  103. cmdbox/web/filer.html +37 -12
  104. cmdbox/web/gui.html +36 -53
  105. cmdbox/web/result.html +24 -3
  106. cmdbox/web/signin.html +35 -14
  107. cmdbox/web/users.html +21 -3
  108. {cmdbox-0.5.1.2.dist-info → cmdbox-0.5.3.dist-info}/METADATA +28 -5
  109. {cmdbox-0.5.1.2.dist-info → cmdbox-0.5.3.dist-info}/RECORD +142 -103
  110. {cmdbox-0.5.1.2.dist-info → cmdbox-0.5.3.dist-info}/entry_points.txt +0 -1
  111. cmdbox/licenses/LICENSE.nh3.0.2.18(MIT).txt +0 -1
  112. /cmdbox/licenses/{LICENSE.Jinja2.3.1.4(BSD License).txt → LICENSE.Jinja2.3.1.6(BSD License).txt} +0 -0
  113. /cmdbox/licenses/{LICENSE.Pygments.2.18.0(BSD License).txt → LICENSE.Pygments.2.19.1(BSD License).txt} +0 -0
  114. /cmdbox/licenses/{LICENSE.anyio.4.6.2.post1(MIT License).txt → LICENSE.anyio.4.9.0(MIT License).txt} +0 -0
  115. /cmdbox/licenses/{LICENSE.argcomplete.3.5.1(Apache Software License).txt → LICENSE.argcomplete.3.6.1(Apache Software License).txt} +0 -0
  116. /cmdbox/licenses/{LICENSE.certifi.2024.8.30(Mozilla Public License 2.0 (MPL 2.0)).txt → LICENSE.certifi.2025.1.31(Mozilla Public License 2.0 (MPL 2.0)).txt} +0 -0
  117. /cmdbox/licenses/{LICENSE.click.8.1.7(BSD License).txt → LICENSE.click.8.1.8(BSD License).txt} +0 -0
  118. /cmdbox/licenses/{LICENSE.cryptography.43.0.3(Apache Software License; BSD License).txt → LICENSE.cryptography.44.0.2(Apache Software License; BSD License).txt} +0 -0
  119. /cmdbox/licenses/{LICENSE.fastapi.0.115.5(MIT License).txt → LICENSE.fastapi.0.115.12(MIT License).txt} +0 -0
  120. /cmdbox/licenses/{LICENSE.importlib_metadata.8.5.0(Apache Software License).txt → LICENSE.id.1.5.0(Apache Software License).txt} +0 -0
  121. /cmdbox/licenses/{LICENSE.keyring.25.5.0(MIT License).txt → LICENSE.keyring.25.6.0(MIT License).txt} +0 -0
  122. /cmdbox/licenses/{LICENSE.more-itertools.10.5.0(MIT License).txt → LICENSE.more-itertools.10.6.0(MIT License).txt} +0 -0
  123. /cmdbox/licenses/{LICENSE.numpy.2.1.3(BSD License).txt → LICENSE.numpy.2.2.4(BSD License).txt} +0 -0
  124. /cmdbox/licenses/{LICENSE.prettytable.3.12.0(BSD License).txt → LICENSE.prettytable.3.16.0(UNKNOWN).txt} +0 -0
  125. /cmdbox/licenses/{LICENSE.pydantic.2.10.2(MIT License).txt → LICENSE.pydantic.2.11.1(MIT License).txt} +0 -0
  126. /cmdbox/licenses/{LICENSE.pydantic_core.2.27.1(MIT License).txt → LICENSE.pydantic_core.2.33.0(MIT License).txt} +0 -0
  127. /cmdbox/licenses/{LICENSE.python-dotenv.1.0.1(BSD License).txt → LICENSE.python-dotenv.1.1.0(BSD License).txt} +0 -0
  128. /cmdbox/licenses/{LICENSE.python-multipart.0.0.17(Apache Software License).txt → LICENSE.python-multipart.0.0.20(Apache Software License).txt} +0 -0
  129. /cmdbox/licenses/{LICENSE.redis.5.2.0(MIT License).txt → LICENSE.redis.5.2.1(MIT License).txt} +0 -0
  130. /cmdbox/licenses/{LICENSE.rich.13.9.4(MIT License).txt → LICENSE.rich.14.0.0(MIT License).txt} +0 -0
  131. /cmdbox/licenses/{LICENSE.sphinx-intl.2.3.0(BSD License).txt → LICENSE.sphinx-intl.2.3.1(BSD License).txt} +0 -0
  132. /cmdbox/licenses/{LICENSE.starlette.0.41.3(BSD License).txt → LICENSE.starlette.0.46.1(BSD License).txt} +0 -0
  133. /cmdbox/licenses/{LICENSE.tomli.2.1.0(MIT License).txt → LICENSE.tomli.2.2.1(MIT License).txt} +0 -0
  134. /cmdbox/licenses/{LICENSE.twine.5.1.1(Apache Software License).txt → LICENSE.twine.6.1.0(Apache Software License).txt} +0 -0
  135. /cmdbox/licenses/{LICENSE.typing_extensions.4.12.2(Python Software Foundation License).txt → LICENSE.typing_extensions.4.13.0(UNKNOWN).txt} +0 -0
  136. /cmdbox/licenses/{LICENSE.urllib3.2.2.3(MIT License).txt → LICENSE.urllib3.2.3.0(MIT License).txt} +0 -0
  137. /cmdbox/licenses/{LICENSE.uvicorn.0.32.1(BSD License).txt → LICENSE.uvicorn.0.34.0(BSD License).txt} +0 -0
  138. /cmdbox/licenses/{LICENSE.watchfiles.1.0.0(MIT License).txt → LICENSE.watchfiles.1.0.4(MIT License).txt} +0 -0
  139. /cmdbox/licenses/{LICENSE.websockets.14.1(BSD License).txt → LICENSE.websockets.15.0.1(BSD License).txt} +0 -0
  140. /cmdbox/licenses/{LICENSE.zope.interface.7.1.1(Zope Public License).txt → LICENSE.zope.interface.7.2(Zope Public License).txt} +0 -0
  141. {cmdbox-0.5.1.2.dist-info → cmdbox-0.5.3.dist-info}/LICENSE +0 -0
  142. {cmdbox-0.5.1.2.dist-info → cmdbox-0.5.3.dist-info}/WHEEL +0 -0
  143. {cmdbox-0.5.1.2.dist-info → cmdbox-0.5.3.dist-info}/top_level.txt +0 -0
@@ -1,42 +1,43 @@
1
- from cmdbox.app import common
2
- from cmdbox.app.commons import convert
3
- from cmdbox.app.features.web import cmdbox_web_exec_cmd
4
- from cmdbox.app.web import Web
5
- from fastapi import FastAPI, Request, Response, HTTPException
6
- from fastapi.responses import StreamingResponse
7
- from pathlib import Path
8
- import io
9
- import urllib.parse
10
-
11
-
12
- class FilerDownload(cmdbox_web_exec_cmd.ExecCmd):
13
- def route(self, web:Web, app:FastAPI) -> None:
14
- """
15
- webモードのルーティングを設定します
16
-
17
- Args:
18
- web (Web): Webオブジェクト
19
- app (FastAPI): FastAPIオブジェクト
20
- """
21
- @app.get('/filer/download/{constr:str}', response_class=StreamingResponse)
22
- async def filer_download(constr:str, req:Request, res:Response):
23
- signin = web.signin.check_signin(req, res)
24
- if signin is not None:
25
- raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
26
- try:
27
- host, port, svname, password, path, scope, img_thumbnail = convert.b64str2str(constr).split('\t')
28
- path = urllib.parse.unquote(path)
29
- data_dir = web.data if scope == 'client' else Path.cwd()
30
- data_dir = None if scope == 'server' else data_dir
31
- opt = dict(host=host, port=port, svname=svname, password=password, svpath=path, scope=scope,
32
- img_thumbnail=img_thumbnail, mode='client', cmd='file_download', client_data=data_dir, stdout_log=False)
33
- opt['capture_stdout'] = nothread = True
34
- ret = self.exec_cmd(req, res, web, 'file_download', opt, nothread, self.appcls)
35
- if len(ret) == 0 or 'success' not in ret[0] or 'data' not in ret[0]['success']:
36
- return common.to_str(ret)
37
- mime = ret[0]['success']['mime_type']
38
- return StreamingResponse(io.BytesIO(convert.b64str2bytes(ret[0]['success']['data'])),
39
- headers={'Cache-Control':'no-cache'},
40
- media_type=mime)
41
- except Exception as e:
42
- raise HTTPException(status_code=404, detail='Missing specified file or not an image.') from e
1
+ from cmdbox.app import common
2
+ from cmdbox.app.commons import convert
3
+ from cmdbox.app.features.web import cmdbox_web_exec_cmd
4
+ from cmdbox.app.web import Web
5
+ from fastapi import FastAPI, Request, Response, HTTPException
6
+ from fastapi.responses import StreamingResponse
7
+ from pathlib import Path
8
+ import io
9
+ import urllib.parse
10
+
11
+
12
+ class FilerDownload(cmdbox_web_exec_cmd.ExecCmd):
13
+ def route(self, web:Web, app:FastAPI) -> None:
14
+ """
15
+ webモードのルーティングを設定します
16
+
17
+ Args:
18
+ web (Web): Webオブジェクト
19
+ app (FastAPI): FastAPIオブジェクト
20
+ """
21
+ @app.get('/filer/download/{constr:str}', response_class=StreamingResponse)
22
+ async def filer_download(constr:str, req:Request, res:Response):
23
+ signin = web.signin.check_signin(req, res)
24
+ if signin is not None:
25
+ raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
26
+ try:
27
+ host, port, svname, password, path, scope, img_thumbnail = convert.b64str2str(constr).split('\t')
28
+ path = urllib.parse.unquote(path)
29
+ data_dir = web.data if scope == 'client' else Path.cwd()
30
+ data_dir = None if scope == 'server' else data_dir
31
+ opt = dict(host=host, port=port, svname=svname, password=password, svpath=path, scope=scope,
32
+ img_thumbnail=img_thumbnail, mode='client', cmd='file_download', client_data=data_dir, stdout_log=False)
33
+ opt['capture_stdout'] = nothread = True
34
+ web.options.audit_exec(req, res, web)
35
+ ret = self.exec_cmd(req, res, web, 'file_download', opt, nothread, self.appcls)
36
+ if len(ret) == 0 or 'success' not in ret[0] or 'data' not in ret[0]['success']:
37
+ return common.to_str(ret)
38
+ mime = ret[0]['success']['mime_type']
39
+ return StreamingResponse(io.BytesIO(convert.b64str2bytes(ret[0]['success']['data'])),
40
+ headers={'Cache-Control':'no-cache'},
41
+ media_type=mime)
42
+ except Exception as e:
43
+ raise HTTPException(status_code=404, detail='Missing specified file or not an image.') from e
@@ -27,6 +27,7 @@ class Filer(feature.WebFeature):
27
27
  if signin is not None:
28
28
  return signin
29
29
  res.headers['Access-Control-Allow-Origin'] = '*'
30
+ web.options.audit_exec(req, res, web)
30
31
  return web.filer_html_data
31
32
 
32
33
  def toolmenu(self, web:Web) -> Dict[str, Any]:
@@ -1,64 +1,65 @@
1
- from cmdbox.app.features.web import cmdbox_web_exec_cmd
2
- from cmdbox.app.web import Web
3
- from fastapi import FastAPI, Request, Response, HTTPException
4
- from fastapi.responses import PlainTextResponse
5
- from pathlib import Path
6
- from starlette.datastructures import UploadFile
7
- import tempfile
8
- import shutil
9
-
10
-
11
- class FilerUpload(cmdbox_web_exec_cmd.ExecCmd):
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
- @app.post('/filer/upload', response_class=PlainTextResponse)
21
- async def filer_upload(req:Request, res:Response):
22
- signin = web.signin.check_signin(req, res)
23
- if signin is not None:
24
- raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
25
- return await self.filer_upload(web, req, res)
26
-
27
- async def filer_upload(self, web:Web, req:Request, res:Response) -> str:
28
- """
29
- ファイルをアップロードする
30
-
31
- Args:
32
- web (Web): Webオブジェクト
33
- req (Request): リクエスト
34
- res (Response): レスポンス
35
-
36
- Returns:
37
- str: 結果
38
- """
39
- q = req.query_params
40
- svpath = q['svpath']
41
- web.logger.info(f"filer_upload: svpath={svpath}")
42
- opt = dict(mode='client', cmd='file_upload',
43
- host=q['host'], port=q['port'], password=q['password'], svname=q['svname'],
44
- scope=q["scope"], client_data=q['client_data'], orverwrite=('orverwrite' in q))
45
- form = await req.form()
46
- with tempfile.TemporaryDirectory() as tmpdir:
47
- for _, fv in form.multi_items():
48
- if not isinstance(fv, UploadFile): continue
49
- raw_filename = fv.filename.replace('\\','/').replace('//','/')
50
- raw_filename = raw_filename if not raw_filename.startswith('/') else raw_filename[1:]
51
- upload_file:Path = Path(tmpdir) / raw_filename
52
- if not upload_file.parent.exists():
53
- upload_file.parent.mkdir(parents=True)
54
- opt['svpath'] = str(svpath / Path(raw_filename).parent)
55
- opt['upload_file'] = str(upload_file).replace('"','')
56
- opt['capture_stdout'] = True
57
- shutil.copyfileobj(fv.file, Path(opt['upload_file']).open('wb'))
58
- ret = self.exec_cmd(req, res, web, "file_upload", opt, nothread=True)
59
- if type(ret) is dict and 'success' not in ret:
60
- return str(ret)
61
- if type(ret) is list and (len(ret) == 0 or 'success' not in ret[0]):
62
- return str(ret)
63
- return 'upload success'
64
- #return f'upload {upload.filename}'
1
+ from cmdbox.app.features.web import cmdbox_web_exec_cmd
2
+ from cmdbox.app.web import Web
3
+ from fastapi import FastAPI, Request, Response, HTTPException
4
+ from fastapi.responses import PlainTextResponse
5
+ from pathlib import Path
6
+ from starlette.datastructures import UploadFile
7
+ import tempfile
8
+ import shutil
9
+
10
+
11
+ class FilerUpload(cmdbox_web_exec_cmd.ExecCmd):
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
+ @app.post('/filer/upload', response_class=PlainTextResponse)
21
+ async def filer_upload(req:Request, res:Response):
22
+ signin = web.signin.check_signin(req, res)
23
+ if signin is not None:
24
+ raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
25
+ return await self.filer_upload(web, req, res)
26
+
27
+ async def filer_upload(self, web:Web, req:Request, res:Response) -> str:
28
+ """
29
+ ファイルをアップロードする
30
+
31
+ Args:
32
+ web (Web): Webオブジェクト
33
+ req (Request): リクエスト
34
+ res (Response): レスポンス
35
+
36
+ Returns:
37
+ str: 結果
38
+ """
39
+ q = req.query_params
40
+ svpath = q['svpath']
41
+ web.logger.info(f"filer_upload: svpath={svpath}")
42
+ opt = dict(mode='client', cmd='file_upload',
43
+ host=q['host'], port=q['port'], password=q['password'], svname=q['svname'],
44
+ scope=q["scope"], client_data=q['client_data'], orverwrite=('orverwrite' in q))
45
+ form = await req.form()
46
+ with tempfile.TemporaryDirectory() as tmpdir:
47
+ for _, fv in form.multi_items():
48
+ if not isinstance(fv, UploadFile): continue
49
+ raw_filename = fv.filename.replace('\\','/').replace('//','/')
50
+ raw_filename = raw_filename if not raw_filename.startswith('/') else raw_filename[1:]
51
+ upload_file:Path = Path(tmpdir) / raw_filename
52
+ if not upload_file.parent.exists():
53
+ upload_file.parent.mkdir(parents=True)
54
+ opt['svpath'] = str(svpath / Path(raw_filename).parent)
55
+ opt['upload_file'] = str(upload_file).replace('"','')
56
+ opt['capture_stdout'] = True
57
+ shutil.copyfileobj(fv.file, Path(opt['upload_file']).open('wb'))
58
+ web.options.audit_exec(req, res, web)
59
+ ret = self.exec_cmd(req, res, web, "file_upload", opt, nothread=True)
60
+ if type(ret) is dict and 'success' not in ret:
61
+ return str(ret)
62
+ if type(ret) is list and (len(ret) == 0 or 'success' not in ret[0]):
63
+ return str(ret)
64
+ return 'upload success'
65
+ #return f'upload {upload.filename}'
@@ -1,165 +1,166 @@
1
- from cmdbox import version
2
- from cmdbox.app import common, feature
3
- from cmdbox.app.web import Web
4
- from fastapi import FastAPI, Request, Response, HTTPException
5
- from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse
6
- from typing import Dict, Any
7
- import logging
8
-
9
-
10
- class Gui(feature.WebFeature):
11
- def __init__(self, appcls, ver):
12
- super().__init__(appcls=appcls, ver=ver)
13
- self.version_info = [dict(tabid='versions_cmdbox', title=version.__appid__,
14
- thisapp=True if version.__appid__ == ver.__appid__ else False,
15
- icon=f'assets/cmdbox/icon.png', url='versions_cmdbox')]
16
-
17
- def route(self, web:Web, app:FastAPI) -> None:
18
- """
19
- webモードのルーティングを設定します
20
-
21
- Args:
22
- web (Web): Webオブジェクト
23
- app (FastAPI): FastAPIオブジェクト
24
- """
25
- if web.gui_html is not None:
26
- if not web.gui_html.is_file():
27
- raise FileNotFoundError(f'gui_html is not found. ({web.gui_html})')
28
- with open(web.gui_html, 'r', encoding='utf-8') as f:
29
- web.gui_html_data = f.read()
30
-
31
- @app.get('/', response_class=HTMLResponse)
32
- async def index(req:Request, res:Response):
33
- return RedirectResponse(url='/gui')
34
-
35
- @app.get('/gui', response_class=HTMLResponse)
36
- @app.post('/gui', response_class=HTMLResponse)
37
- async def gui(req:Request, res:Response):
38
- signin = web.signin.check_signin(req, res)
39
- if signin is not None:
40
- return signin
41
- res.headers['Access-Control-Allow-Origin'] = '*'
42
- return web.gui_html_data
43
-
44
- @app.get('/signin/gui/appid', response_class=PlainTextResponse)
45
- @app.get('/gui/appid', response_class=PlainTextResponse)
46
- async def appid(req:Request, res:Response):
47
- return self.ver.__appid__
48
-
49
- @app.get('/gui/version_info')
50
- async def version_info(req:Request, res:Response):
51
- return self.version_info
52
-
53
- @app.get('/gui/user_info')
54
- async def user_info(req:Request, res:Response):
55
- if 'signin' not in req.session:
56
- raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
57
- if 'name' not in req.session['signin']:
58
- raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
59
- name = req.session['signin']['name']
60
- try:
61
- users = web.user_list(name)
62
- if users is None or len(users) == 0:
63
- return dict(warn='User information is not found.')
64
- return users[0]
65
- except Exception as e:
66
- return dict(error=f'{e}')
67
-
68
- @app.get('/gui/filemenu')
69
- async def filemenu(req:Request, res:Response):
70
- return web.filemenu
71
-
72
- @app.get('/gui/toolmenu')
73
- async def toolmenu(req:Request, res:Response):
74
- ret = dict()
75
- for k, v in web.toolmenu.items():
76
- path_jadge = web.signin.check_path(req, v['href'])
77
- if path_jadge is not None:
78
- continue
79
- ret[k] = v
80
- return ret
81
-
82
- @app.get('/gui/viewmenu')
83
- async def viewmenu(req:Request, res:Response):
84
- return web.viewmenu
85
-
86
- @app.get('/gui/aboutmenu')
87
- async def aboutmenu(req:Request, res:Response):
88
- return web.aboutmenu
89
-
90
- def callback_console_modal_log_func(self, web:Web, output:Dict[str, Any]):
91
- """
92
- コンソールモーダルにログを出力する
93
-
94
- Args:
95
- web (Web): Webオブジェクト
96
- output (Dict[str, Any]): 出力
97
- """
98
- if web.logger.level == logging.DEBUG:
99
- output_str = common.to_str(output, slise=100)
100
- web.logger.debug(f"web.callback_console_modal_log_func: output={output_str}")
101
- web.cb_queue.put(('js_console_modal_log_func', None, output))
102
-
103
- def callback_return_cmd_exec_func(self, web:Web, title:str, output:Dict[str, Any]):
104
- """
105
- コマンド実行結果を返す
106
-
107
- Args:
108
- web (Web): Webオブジェクト
109
- title (str): タイトル
110
- output (Dict[str, Any]): 出力
111
- """
112
- if web.logger.level == logging.DEBUG:
113
- output_str = common.to_str(output, slise=100)
114
- web.logger.debug(f"web.callback_return_cmd_exec_func: output={output_str}")
115
- web.cb_queue.put(('js_return_cmd_exec_func', title, output))
116
-
117
- def callback_return_pipe_exec_func(self, web:Web, title:str, output:Dict[str, Any]):
118
- """
119
- パイプライン実行結果を返す
120
-
121
- Args:
122
- web (Web): Webオブジェクト
123
- title (str): タイトル
124
- output (Dict[str, Any]): 出力
125
- """
126
- if web.logger.level == logging.DEBUG:
127
- output_str = common.to_str(output, slise=100)
128
- web.logger.debug(f"web.callback_return_pipe_exec_func: title={title}, output={output_str}")
129
- web.cb_queue.put(('js_return_pipe_exec_func', title, output))
130
-
131
- def callback_return_stream_log_func(self, web:Web, output:Dict[str, Any]):
132
- """
133
- ストリームログを返す
134
-
135
- Args:
136
- web (Web): Webオブジェクト
137
- output (Dict[str, Any]): 出力
138
- """
139
- if web.logger.level == logging.DEBUG:
140
- output_str = common.to_str(output, slise=100)
141
- web.logger.debug(f"web.callback_return_stream_log_func: output={output_str}")
142
- web.cb_queue.put(('js_return_stream_log_func', None, output))
143
-
144
- def mk_curl_fileup(self, web:Web, cmd_opt:Dict[str, Any]) -> str:
145
- """
146
- curlコマンド文字列を作成する
147
-
148
- Args:
149
- web (Web): Webオブジェクト
150
- cmd_opt (dict): コマンドのオプション
151
-
152
- Returns:
153
- str: curlコマンド文字列
154
- """
155
- if 'mode' not in cmd_opt or 'cmd' not in cmd_opt:
156
- return ""
157
- curl_fileup = set()
158
- for ref in web.options.get_cmd_choices(cmd_opt['mode'], cmd_opt['cmd'], True):
159
- if 'fileio' not in ref or ref['fileio'] != 'in':
160
- continue
161
- if ref['opt'] in cmd_opt and cmd_opt[ref['opt']] != '':
162
- curl_fileup.add(f'-F "{ref["opt"]}=@<{ref["opt"]}>"')
163
- if 'stdin' in cmd_opt and cmd_opt['stdin']:
164
- curl_fileup.add(f'-F "input_file=@<input_file>"')
165
- return " ".join(curl_fileup)
1
+ from cmdbox import version
2
+ from cmdbox.app import common, feature
3
+ from cmdbox.app.web import Web
4
+ from fastapi import FastAPI, Request, Response, HTTPException
5
+ from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse
6
+ from typing import Dict, Any
7
+ import logging
8
+
9
+
10
+ class Gui(feature.WebFeature):
11
+ def __init__(self, appcls, ver):
12
+ super().__init__(appcls=appcls, ver=ver)
13
+ self.version_info = [dict(tabid='versions_cmdbox', title=version.__appid__,
14
+ thisapp=True if version.__appid__ == ver.__appid__ else False,
15
+ icon=f'assets/cmdbox/icon.png', url='versions_cmdbox')]
16
+
17
+ def route(self, web:Web, app:FastAPI) -> None:
18
+ """
19
+ webモードのルーティングを設定します
20
+
21
+ Args:
22
+ web (Web): Webオブジェクト
23
+ app (FastAPI): FastAPIオブジェクト
24
+ """
25
+ if web.gui_html is not None:
26
+ if not web.gui_html.is_file():
27
+ raise FileNotFoundError(f'gui_html is not found. ({web.gui_html})')
28
+ with open(web.gui_html, 'r', encoding='utf-8') as f:
29
+ web.gui_html_data = f.read()
30
+
31
+ @app.get('/', response_class=HTMLResponse)
32
+ async def index(req:Request, res:Response):
33
+ return RedirectResponse(url='/gui')
34
+
35
+ @app.get('/gui', response_class=HTMLResponse)
36
+ @app.post('/gui', response_class=HTMLResponse)
37
+ async def gui(req:Request, res:Response):
38
+ signin = web.signin.check_signin(req, res)
39
+ if signin is not None:
40
+ return signin
41
+ res.headers['Access-Control-Allow-Origin'] = '*'
42
+ web.options.audit_exec(req, res, web)
43
+ return web.gui_html_data
44
+
45
+ @app.get('/signin/gui/appid', response_class=PlainTextResponse)
46
+ @app.get('/gui/appid', response_class=PlainTextResponse)
47
+ async def appid(req:Request, res:Response):
48
+ return self.ver.__appid__
49
+
50
+ @app.get('/gui/version_info')
51
+ async def version_info(req:Request, res:Response):
52
+ return self.version_info
53
+
54
+ @app.get('/gui/user_info')
55
+ async def user_info(req:Request, res:Response):
56
+ if 'signin' not in req.session:
57
+ raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
58
+ if 'name' not in req.session['signin']:
59
+ raise HTTPException(status_code=401, detail=self.DEFAULT_401_MESSAGE)
60
+ name = req.session['signin']['name']
61
+ try:
62
+ users = web.user_list(name)
63
+ if users is None or len(users) == 0:
64
+ return dict(warn='User information is not found.')
65
+ return users[0]
66
+ except Exception as e:
67
+ return dict(error=f'{e}')
68
+
69
+ @app.get('/gui/filemenu')
70
+ async def filemenu(req:Request, res:Response):
71
+ return web.filemenu
72
+
73
+ @app.get('/gui/toolmenu')
74
+ async def toolmenu(req:Request, res:Response):
75
+ ret = dict()
76
+ for k, v in web.toolmenu.items():
77
+ path_jadge = web.signin.check_path(req, v['href'])
78
+ if path_jadge is not None:
79
+ continue
80
+ ret[k] = v
81
+ return ret
82
+
83
+ @app.get('/gui/viewmenu')
84
+ async def viewmenu(req:Request, res:Response):
85
+ return web.viewmenu
86
+
87
+ @app.get('/gui/aboutmenu')
88
+ async def aboutmenu(req:Request, res:Response):
89
+ return web.aboutmenu
90
+
91
+ def callback_console_modal_log_func(self, web:Web, output:Dict[str, Any]):
92
+ """
93
+ コンソールモーダルにログを出力する
94
+
95
+ Args:
96
+ web (Web): Webオブジェクト
97
+ output (Dict[str, Any]): 出力
98
+ """
99
+ if web.logger.level == logging.DEBUG:
100
+ output_str = common.to_str(output, slise=100)
101
+ web.logger.debug(f"web.callback_console_modal_log_func: output={output_str}")
102
+ web.cb_queue.put(('js_console_modal_log_func', None, output))
103
+
104
+ def callback_return_cmd_exec_func(self, web:Web, title:str, output:Dict[str, Any]):
105
+ """
106
+ コマンド実行結果を返す
107
+
108
+ Args:
109
+ web (Web): Webオブジェクト
110
+ title (str): タイトル
111
+ output (Dict[str, Any]): 出力
112
+ """
113
+ if web.logger.level == logging.DEBUG:
114
+ output_str = common.to_str(output, slise=100)
115
+ web.logger.debug(f"web.callback_return_cmd_exec_func: output={output_str}")
116
+ web.cb_queue.put(('js_return_cmd_exec_func', title, output))
117
+
118
+ def callback_return_pipe_exec_func(self, web:Web, title:str, output:Dict[str, Any]):
119
+ """
120
+ パイプライン実行結果を返す
121
+
122
+ Args:
123
+ web (Web): Webオブジェクト
124
+ title (str): タイトル
125
+ output (Dict[str, Any]): 出力
126
+ """
127
+ if web.logger.level == logging.DEBUG:
128
+ output_str = common.to_str(output, slise=100)
129
+ web.logger.debug(f"web.callback_return_pipe_exec_func: title={title}, output={output_str}")
130
+ web.cb_queue.put(('js_return_pipe_exec_func', title, output))
131
+
132
+ def callback_return_stream_log_func(self, web:Web, output:Dict[str, Any]):
133
+ """
134
+ ストリームログを返す
135
+
136
+ Args:
137
+ web (Web): Webオブジェクト
138
+ output (Dict[str, Any]): 出力
139
+ """
140
+ if web.logger.level == logging.DEBUG:
141
+ output_str = common.to_str(output, slise=100)
142
+ web.logger.debug(f"web.callback_return_stream_log_func: output={output_str}")
143
+ web.cb_queue.put(('js_return_stream_log_func', None, output))
144
+
145
+ def mk_curl_fileup(self, web:Web, cmd_opt:Dict[str, Any]) -> str:
146
+ """
147
+ curlコマンド文字列を作成する
148
+
149
+ Args:
150
+ web (Web): Webオブジェクト
151
+ cmd_opt (dict): コマンドのオプション
152
+
153
+ Returns:
154
+ str: curlコマンド文字列
155
+ """
156
+ if 'mode' not in cmd_opt or 'cmd' not in cmd_opt:
157
+ return ""
158
+ curl_fileup = set()
159
+ for ref in web.options.get_cmd_choices(cmd_opt['mode'], cmd_opt['cmd'], True):
160
+ if 'fileio' not in ref or ref['fileio'] != 'in':
161
+ continue
162
+ if ref['opt'] in cmd_opt and cmd_opt[ref['opt']] != '':
163
+ curl_fileup.add(f'-F "{ref["opt"]}=@<{ref["opt"]}>"')
164
+ if 'stdin' in cmd_opt and cmd_opt['stdin']:
165
+ curl_fileup.add(f'-F "input_file=@<input_file>"')
166
+ return " ".join(curl_fileup)