cmdbox 0.5.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 (51) hide show
  1. cmdbox/app/auth/signin.py +1 -0
  2. cmdbox/app/features/cli/audit_base.py +4 -1
  3. cmdbox/app/features/cli/cmdbox_audit_delete.py +27 -18
  4. cmdbox/app/features/cli/cmdbox_audit_search.py +128 -62
  5. cmdbox/app/features/cli/cmdbox_audit_write.py +27 -20
  6. cmdbox/app/features/web/cmdbox_web_audit.py +81 -0
  7. cmdbox/app/features/web/cmdbox_web_audit_metrics.py +72 -0
  8. cmdbox/app/features/web/cmdbox_web_exec_cmd.py +10 -5
  9. cmdbox/app/features/web/cmdbox_web_user_data.py +58 -0
  10. cmdbox/app/options.py +40 -19
  11. cmdbox/app/web.py +7 -1
  12. cmdbox/extensions/features.yml +7 -4
  13. cmdbox/extensions/user_list.yml +5 -0
  14. cmdbox/licenses/LICENSE.argcomplete.3.6.2(Apache Software License).txt +177 -0
  15. cmdbox/licenses/LICENSE.gevent.25.4.1(MIT).txt +25 -0
  16. cmdbox/licenses/LICENSE.greenlet.3.2.0(MIT AND Python-2.0).txt +30 -0
  17. cmdbox/licenses/LICENSE.pillow.11.2.1(UNKNOWN).txt +1200 -0
  18. cmdbox/licenses/LICENSE.prompt_toolkit.3.0.51(BSD License).txt +27 -0
  19. cmdbox/licenses/LICENSE.pydantic.2.11.3(MIT License).txt +21 -0
  20. cmdbox/licenses/LICENSE.pydantic_core.2.33.1(MIT License).txt +21 -0
  21. cmdbox/licenses/LICENSE.starlette.0.46.2(BSD License).txt +27 -0
  22. cmdbox/licenses/LICENSE.typing_extensions.4.13.2(UNKNOWN).txt +279 -0
  23. cmdbox/licenses/LICENSE.urllib3.2.4.0(UNKNOWN).txt +21 -0
  24. cmdbox/licenses/LICENSE.uvicorn.0.34.1(BSD License).txt +27 -0
  25. cmdbox/licenses/LICENSE.watchfiles.1.0.5(MIT License).txt +21 -0
  26. cmdbox/licenses/files.txt +12 -13
  27. cmdbox/version.py +2 -2
  28. cmdbox/web/assets/apexcharts/apexcharts.css +679 -0
  29. cmdbox/web/assets/apexcharts/apexcharts.min.js +38 -0
  30. cmdbox/web/assets/cmdbox/audit.js +340 -0
  31. cmdbox/web/assets/cmdbox/color_mode.css +4 -0
  32. cmdbox/web/assets/cmdbox/common.js +397 -24
  33. cmdbox/web/assets/cmdbox/filer_modal.js +1 -1
  34. cmdbox/web/assets/cmdbox/list_cmd.js +7 -271
  35. cmdbox/web/assets/cmdbox/list_pipe.js +3 -3
  36. cmdbox/web/assets/cmdbox/users.js +17 -17
  37. cmdbox/web/assets/cmdbox/view_raw.js +1 -1
  38. cmdbox/web/assets/cmdbox/view_result.js +11 -13
  39. cmdbox/web/assets/filer/filer.js +2 -2
  40. cmdbox/web/assets_license_list.txt +4 -1
  41. cmdbox/web/audit.html +268 -0
  42. cmdbox/web/filer.html +21 -10
  43. cmdbox/web/gui.html +21 -52
  44. cmdbox/web/result.html +9 -2
  45. cmdbox/web/users.html +7 -3
  46. {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.dist-info}/METADATA +8 -5
  47. {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.dist-info}/RECORD +51 -32
  48. {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.dist-info}/LICENSE +0 -0
  49. {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.dist-info}/WHEEL +0 -0
  50. {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.dist-info}/entry_points.txt +0 -0
  51. {cmdbox-0.5.2.dist-info → cmdbox-0.5.3.dist-info}/top_level.txt +0 -0
cmdbox/app/auth/signin.py CHANGED
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  from typing import Dict, Any, Tuple, List, Union
6
6
  import copy
7
7
  import logging
8
+ import string
8
9
 
9
10
 
10
11
  class Signin(object):
@@ -8,7 +8,8 @@ import sqlite3
8
8
 
9
9
 
10
10
  class AuditBase(feature.ResultEdgeFeature):
11
-
11
+ TBL_COLS = ['audit_type', 'clmsg_id', 'clmsg_date', 'clmsg_src', 'clmsg_title', 'clmsg_user', 'clmsg_body', 'clmsg_tag', 'svmsg_id', 'svmsg_date']
12
+ DT_FMT = ['%Y/%m/%d %H:%M', '%Y/%m/%d %H', '%Y/%m/%d', '%Y/%m', '%Y', '%m', '%u']
12
13
  def get_option(self):
13
14
  """
14
15
  この機能のオプションを返します
@@ -97,6 +98,7 @@ class AuditBase(feature.ResultEdgeFeature):
97
98
  clmsg_id TEXT,
98
99
  clmsg_date TIMESTAMP WITH TIME ZONE,
99
100
  clmsg_src TEXT,
101
+ clmsg_title TEXT,
100
102
  clmsg_user TEXT,
101
103
  clmsg_body JSON,
102
104
  clmsg_tag JSON,
@@ -123,6 +125,7 @@ class AuditBase(feature.ResultEdgeFeature):
123
125
  clmsg_id TEXT,
124
126
  clmsg_date TEXT,
125
127
  clmsg_src TEXT,
128
+ clmsg_title TEXT,
126
129
  clmsg_user TEXT,
127
130
  clmsg_body JSON,
128
131
  clmsg_tag JSON,
@@ -41,7 +41,7 @@ class AuditDelete(audit_base.AuditBase):
41
41
  opt['discription_ja'] = "監査ログを削除します。"
42
42
  opt['discription_en'] = "Delete the audit log."
43
43
  opt['choice'] += [
44
- dict(opt="delete_audit_type", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=Options.AUDITS,
44
+ dict(opt="delete_audit_type", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=['']+Options.AUDITS,
45
45
  discription_ja="削除条件の監査の種類を指定します。",
46
46
  discription_en="Specifies the type of audit for the delete condition."),
47
47
  dict(opt="delete_clmsg_id", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
@@ -56,6 +56,9 @@ class AuditDelete(audit_base.AuditBase):
56
56
  dict(opt="delete_clmsg_src", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
57
57
  discription_ja="削除条件のクライアントのメッセージの発生源を指定します。LIKE検索を行います。",
58
58
  discription_en="Specifies the source of the message for the client in the delete condition; performs a LIKE search."),
59
+ dict(opt="delete_clmsg_title", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
60
+ discription_ja="削除条件のクライアントのメッセージタイトルを指定します。LIKE検索を行います。",
61
+ discription_en="Specifies the message title of the client for the deletion condition; a LIKE search is performed."),
59
62
  dict(opt="delete_clmsg_user", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
60
63
  discription_ja="削除条件のクライアントのメッセージの発生させたユーザーを指定します。LIKE検索を行います。",
61
64
  discription_en="Specifies the user who generated the message for the client in the delete condition; performs a LIKE search."),
@@ -111,6 +114,7 @@ class AuditDelete(audit_base.AuditBase):
111
114
  delete_clmsg_edate = args.delete_clmsg_edate+common.get_tzoffset_str() if args.delete_clmsg_edate else None
112
115
  delete_clmsg_edate_b64 = convert.str2b64str(delete_clmsg_edate)
113
116
  delete_clmsg_src_b64 = convert.str2b64str(args.delete_clmsg_src)
117
+ delete_clmsg_title_b64 = convert.str2b64str(args.delete_clmsg_title) if args.delete_clmsg_title else None
114
118
  delete_clmsg_user_b64 = convert.str2b64str(args.delete_clmsg_user)
115
119
  delete_clmsg_body_str = json.dumps(args.delete_clmsg_body, default=common.default_json_enc, ensure_ascii=False) if args.delete_clmsg_body else '{}'
116
120
  delete_clmsg_body_b64 = convert.str2b64str(delete_clmsg_body_str)
@@ -129,8 +133,8 @@ class AuditDelete(audit_base.AuditBase):
129
133
  cl = client.Client(logger, redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname)
130
134
  ret = cl.redis_cli.send_cmd(self.get_svcmd(),
131
135
  [delete_audit_type_b64, delete_clmsg_id_b64, delete_clmsg_sdate_b64, delete_clmsg_edate_b64,
132
- delete_clmsg_src_b64, delete_clmsg_user_b64, delete_clmsg_body_b64, delete_clmsg_tag_b64,
133
- delete_svmsg_id_b64, delete_svmsg_sdate_b64, delete_svmsg_edate_b64,
136
+ delete_clmsg_src_b64, delete_clmsg_title_b64, delete_clmsg_user_b64, delete_clmsg_body_b64,
137
+ delete_clmsg_tag_b64, delete_svmsg_id_b64, delete_svmsg_sdate_b64, delete_svmsg_edate_b64,
134
138
  pg_enabled, pg_host_b64, pg_port, pg_user_b64, pg_password_b64, pg_dbname_b64],
135
139
  retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout)
136
140
  common.print_format(ret, args.format, tm, None, False, pf=pf)
@@ -180,21 +184,22 @@ class AuditDelete(audit_base.AuditBase):
180
184
  delete_clmsg_sdate = convert.b64str2str(msg[4])
181
185
  delete_clmsg_edate = convert.b64str2str(msg[5])
182
186
  delete_clmsg_src = convert.b64str2str(msg[6])
183
- delete_clmsg_user = convert.b64str2str(msg[7])
184
- body = json.loads(convert.b64str2str(msg[8]))
185
- tags = json.loads(convert.b64str2str(msg[9]))
186
- delete_svmsg_id = convert.b64str2str(msg[10])
187
- delete_svmsg_sdate = convert.b64str2str(msg[11])
188
- delete_svmsg_edate = convert.b64str2str(msg[12])
189
- pg_enabled = True if msg[13]=='True' else False
190
- pg_host = convert.b64str2str(msg[14])
191
- pg_port = int(msg[15]) if msg[15]!='None' else None
192
- pg_user = convert.b64str2str(msg[16])
193
- pg_password = convert.b64str2str(msg[17])
194
- pg_dbname = convert.b64str2str(msg[18])
187
+ delete_clmsg_title = convert.b64str2str(msg[7])
188
+ delete_clmsg_user = convert.b64str2str(msg[8])
189
+ body = json.loads(convert.b64str2str(msg[9]))
190
+ tags = json.loads(convert.b64str2str(msg[10]))
191
+ delete_svmsg_id = convert.b64str2str(msg[11])
192
+ delete_svmsg_sdate = convert.b64str2str(msg[12])
193
+ delete_svmsg_edate = convert.b64str2str(msg[13])
194
+ pg_enabled = True if msg[14]=='True' else False
195
+ pg_host = convert.b64str2str(msg[15])
196
+ pg_port = int(msg[16]) if msg[16]!='None' else None
197
+ pg_user = convert.b64str2str(msg[17])
198
+ pg_password = convert.b64str2str(msg[18])
199
+ pg_dbname = convert.b64str2str(msg[19])
195
200
  st = self.delete(msg[1],
196
201
  delete_audit_type, delete_clmsg_id, delete_clmsg_sdate, delete_clmsg_edate,
197
- delete_clmsg_src, delete_clmsg_user, body, tags,
202
+ delete_clmsg_src, delete_clmsg_title, delete_clmsg_user, body, tags,
198
203
  delete_svmsg_id, delete_svmsg_sdate, delete_svmsg_edate,
199
204
  pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname,
200
205
  data_dir, logger, redis_cli)
@@ -202,8 +207,8 @@ class AuditDelete(audit_base.AuditBase):
202
207
 
203
208
  def delete(self, reskey:str,
204
209
  delete_audit_type:str, delete_clmsg_id:str, delete_clmsg_sdate:str, delete_clmsg_edate:str,
205
- delete_clmsg_src:str, delete_clmsg_user:str, delete_clmsg_body:Dict[str, Any], delete_clmsg_tags:List[str],
206
- delete_svmsg_id:str, delete_svmsg_sdate:str, delete_svmsg_edate:str,
210
+ delete_clmsg_src:str, delete_clmsg_title:str, delete_clmsg_user:str, delete_clmsg_body:Dict[str, Any],
211
+ delete_clmsg_tags:List[str], delete_svmsg_id:str, delete_svmsg_sdate:str, delete_svmsg_edate:str,
207
212
  pg_enabled:bool, pg_host:str, pg_port:int, pg_user:str, pg_password:str, pg_dbname:str,
208
213
  data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient) -> int:
209
214
  """
@@ -216,6 +221,7 @@ class AuditDelete(audit_base.AuditBase):
216
221
  delete_clmsg_sdate (str): クライアントメッセージ発生日時(開始)
217
222
  delete_clmsg_edate (str): クライアントメッセージ発生日時(終了)
218
223
  delete_clmsg_src (str): クライアントメッセージの発生源
224
+ delete_clmsg_title (str): クライアントメッセージのタイトル
219
225
  delete_clmsg_user (str): クライアントメッセージの発生させたユーザー
220
226
  delete_clmsg_body (Dict[str, Any]): クライアントメッセージの本文
221
227
  delete_clmsg_tags (List[str]): クライアントメッセージのタグ
@@ -257,6 +263,9 @@ class AuditDelete(audit_base.AuditBase):
257
263
  if delete_clmsg_src and delete_clmsg_src != 'None':
258
264
  where.append(f'clmsg_src LIKE {"%s" if pg_enabled else "?"}')
259
265
  params.append(delete_clmsg_src)
266
+ if delete_clmsg_title and delete_clmsg_title != 'None':
267
+ where.append(f'clmsg_title LIKE {"%s" if pg_enabled else "?"}')
268
+ params.append(delete_clmsg_src)
260
269
  if delete_clmsg_user and delete_clmsg_user != 'None':
261
270
  where.append(f'clmsg_user LIKE {"%s" if pg_enabled else "?"}')
262
271
  params.append(delete_clmsg_user)
@@ -41,11 +41,14 @@ class AuditSearch(audit_base.AuditBase):
41
41
  opt['discription_ja'] = "監査ログを検索します。"
42
42
  opt['discription_en'] = "Search the audit log."
43
43
  opt['choice'] += [
44
- dict(opt="select", type=Options.T_STR, default=None, required=False, multi=True, hide=False,
45
- choice=['', 'audit_type', 'clmsg_id', 'clmsg_date', 'clmsg_tag', 'clmsg_src', 'clmsg_body', 'svmsg_id', 'svmsg_date'],
44
+ dict(opt="select", type=Options.T_DICT, default=None, required=False, multi=True, hide=False,
45
+ choice=dict(key=['']+self.TBL_COLS, val=['-','count','sum','avg','min','max']),
46
46
  discription_ja="取得項目を指定します。指定しない場合は全ての項目を取得します。",
47
47
  discription_en="Specify the items to be retrieved. If not specified, all items are acquired."),
48
- dict(opt="filter_audit_type", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=Options.AUDITS,
48
+ dict(opt="select_date_format", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=['']+self.DT_FMT,
49
+ discription_ja="取得項目の日時のフォーマットを指定します。",
50
+ discription_en="Specifies the format of the date and time of the acquisition item."),
51
+ dict(opt="filter_audit_type", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=['']+Options.AUDITS,
49
52
  discription_ja="フィルタ条件の監査の種類を指定します。",
50
53
  discription_en="Specifies the type of audit for the filter condition."),
51
54
  dict(opt="filter_clmsg_id", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
@@ -60,6 +63,9 @@ class AuditSearch(audit_base.AuditBase):
60
63
  dict(opt="filter_clmsg_src", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
61
64
  discription_ja="フィルタ条件のクライアントのメッセージの発生源を指定します。LIKE検索を行います。",
62
65
  discription_en="Specifies the source of the message for the client in the filter condition; performs a LIKE search."),
66
+ dict(opt="filter_clmsg_title", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
67
+ discription_ja="フィルタ条件のクライアントのメッセージタイトルを指定します。LIKE検索を行います。",
68
+ discription_en="Specifies the message title of the client for the filter condition; a LIKE search is performed."),
63
69
  dict(opt="filter_clmsg_user", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
64
70
  discription_ja="フィルタ条件のクライアントのメッセージの発生させたユーザーを指定します。LIKE検索を行います。",
65
71
  discription_en="Specifies the user who generated the message for the client in the filter condition; performs a LIKE search."),
@@ -78,7 +84,13 @@ class AuditSearch(audit_base.AuditBase):
78
84
  dict(opt="filter_svmsg_edate", type=Options.T_DATETIME, default=None, required=False, multi=False, hide=False, choice=None,
79
85
  discription_ja="フィルタ条件のサーバーのメッセージ発生日時(終了)を指定します。",
80
86
  discription_en="Specify the date and time (end) when the message occurred for the server in the filter condition."),
81
- dict(opt="sort", type=Options.T_DICT, default=None, required=False, multi=True, hide=False, choice=['', 'ASC', 'DICT'],
87
+ dict(opt="groupby", type=Options.T_STR, default=None, required=False, multi=True, hide=False, choice=['']+self.TBL_COLS,
88
+ discription_ja="グループ化項目を指定します。",
89
+ discription_en="Specify grouping items."),
90
+ dict(opt="groupby_date_format", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=['']+self.DT_FMT,
91
+ discription_ja="グループ化項目の日時のフォーマットを指定します。",
92
+ discription_en="Specifies the format of the date and time of the grouping item."),
93
+ dict(opt="sort", type=Options.T_DICT, default=None, required=False, multi=True, hide=False, choice=dict(key=['']+self.TBL_COLS, val=['', 'ASC', 'DESC']),
82
94
  discription_ja="ソート項目を指定します。",
83
95
  discription_en="Specify the sort item."),
84
96
  dict(opt="offset", type=Options.T_INT, default=0, required=False, multi=False, hide=False, choice=None,
@@ -117,43 +129,51 @@ class AuditSearch(audit_base.AuditBase):
117
129
  common.print_format(msg, args.format, tm, None, False, pf=pf)
118
130
  return 1, msg, None
119
131
 
120
- select_str = json.dumps(args.select, default=common.default_json_enc, ensure_ascii=False) if args.select else '[]'
132
+ select_str = json.dumps(args.select, default=common.default_json_enc, ensure_ascii=False) if getattr(args, 'select', None) else '{}'
121
133
  select_b64 = convert.str2b64str(select_str)
122
- sort_str = json.dumps(args.sort, default=common.default_json_enc, ensure_ascii=False) if args.sort else '{}'
134
+ select_date_format_b64 = convert.str2b64str(getattr(args, 'select_date_format', None))
135
+ groupby_str = json.dumps(args.groupby, default=common.default_json_enc, ensure_ascii=False) if getattr(args, 'groupby', None) else '[]'
136
+ groupby_b64 = convert.str2b64str(groupby_str)
137
+ groupby_date_format_b64 = convert.str2b64str(getattr(args, 'groupby_date_format', None))
138
+ args.sort = args.sort if getattr(args, 'sort', {}) else {}
139
+ args.sort = args.sort if isinstance(args.sort, dict) else {str(args.sort): 'DESC'}
140
+ sort_str = json.dumps(args.sort, default=common.default_json_enc, ensure_ascii=False)
123
141
  sort_b64 = convert.str2b64str(sort_str)
124
- offset = args.offset
125
- limit = args.limit
126
- filter_audit_type_b64 = convert.str2b64str(args.filter_audit_type)
127
- filter_clmsg_id_b64 = convert.str2b64str(args.filter_clmsg_id)
128
- filter_clmsg_sdate = args.filter_clmsg_sdate+common.get_tzoffset_str() if args.filter_clmsg_sdate else None
142
+ offset = getattr(args, 'offset', None)
143
+ limit = getattr(args, 'limit', None)
144
+ filter_audit_type_b64 = convert.str2b64str(getattr(args, 'filter_audit_type', None))
145
+ filter_clmsg_id_b64 = convert.str2b64str(getattr(args, 'filter_clmsg_id', None))
146
+ filter_clmsg_sdate = args.filter_clmsg_sdate+common.get_tzoffset_str() if getattr(args, 'filter_clmsg_sdate', None) else None
129
147
  filter_clmsg_sdate_b64 = convert.str2b64str(filter_clmsg_sdate)
130
- filter_clmsg_edate = args.filter_clmsg_edate+common.get_tzoffset_str() if args.filter_clmsg_edate else None
148
+ filter_clmsg_edate = args.filter_clmsg_edate+common.get_tzoffset_str() if getattr(args, 'filter_clmsg_edate', None) else None
131
149
  filter_clmsg_edate_b64 = convert.str2b64str(filter_clmsg_edate)
132
- filter_clmsg_src_b64 = convert.str2b64str(args.filter_clmsg_src)
133
- filter_clmsg_user_b64 = convert.str2b64str(args.filter_clmsg_user)
134
- filter_clmsg_body_str = json.dumps(args.filter_clmsg_body, default=common.default_json_enc, ensure_ascii=False) if args.filter_clmsg_body else '{}'
150
+ filter_clmsg_src_b64 = convert.str2b64str(getattr(args, 'filter_clmsg_src', None))
151
+ filter_clmsg_title_b64 = convert.str2b64str(getattr(args, 'filter_clmsg_title', None))
152
+ filter_clmsg_user_b64 = convert.str2b64str(getattr(args, 'filter_clmsg_user', None))
153
+ filter_clmsg_body_str = json.dumps(args.filter_clmsg_body, default=common.default_json_enc, ensure_ascii=False) if getattr(args, 'filter_clmsg_body', None) else '{}'
135
154
  filter_clmsg_body_b64 = convert.str2b64str(filter_clmsg_body_str)
136
- filter_clmsg_tag_str = json.dumps(args.filter_clmsg_tag, default=common.default_json_enc, ensure_ascii=False) if args.filter_clmsg_tag else '[]'
155
+ filter_clmsg_tag_str = json.dumps(args.filter_clmsg_tag, default=common.default_json_enc, ensure_ascii=False) if getattr(args, 'filter_clmsg_tag', None) else '[]'
137
156
  filter_clmsg_tag_b64 = convert.str2b64str(filter_clmsg_tag_str)
138
- filter_svmsg_id_b64 = convert.str2b64str(args.filter_svmsg_id)
139
- filter_svmsg_sdate_b64 = convert.str2b64str(args.filter_svmsg_sdate)
140
- filter_svmsg_edate_b64 = convert.str2b64str(args.filter_svmsg_edate)
141
- pg_enabled = args.pg_enabled
142
- pg_host_b64 = convert.str2b64str(args.pg_host)
143
- pg_port = args.pg_port if isinstance(args.pg_port, int) else None
144
- pg_user_b64 = convert.str2b64str(args.pg_user)
145
- pg_password_b64 = convert.str2b64str(args.pg_password)
146
- pg_dbname_b64 = convert.str2b64str(args.pg_dbname)
157
+ filter_svmsg_id_b64 = convert.str2b64str(getattr(args, 'filter_svmsg_id', None))
158
+ filter_svmsg_sdate_b64 = convert.str2b64str(getattr(args, 'filter_svmsg_sdate', None))
159
+ filter_svmsg_edate_b64 = convert.str2b64str(getattr(args, 'filter_svmsg_edate', None))
160
+ pg_enabled = args.pg_enabled if getattr(args, 'pg_enabled', False) and isinstance(args.pg_enabled, bool) else False
161
+ pg_host_b64 = convert.str2b64str(getattr(args, 'pg_host', None))
162
+ pg_port = args.pg_port if getattr(args, 'pg_port', 5432) and isinstance(args.pg_port, int) else None
163
+ pg_user_b64 = convert.str2b64str(getattr(args, 'pg_user', None))
164
+ pg_password_b64 = convert.str2b64str(getattr(args, 'pg_password', None))
165
+ pg_dbname_b64 = convert.str2b64str(getattr(args, 'pg_dbname', None))
147
166
 
148
167
  cl = client.Client(logger, redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname)
149
168
  ret = cl.redis_cli.send_cmd(self.get_svcmd(),
150
- [select_b64, sort_b64, str(offset), str(limit),
169
+ [select_b64, select_date_format_b64, groupby_b64, groupby_date_format_b64,
170
+ sort_b64, str(offset), str(limit),
151
171
  filter_audit_type_b64, filter_clmsg_id_b64, filter_clmsg_sdate_b64, filter_clmsg_edate_b64,
152
- filter_clmsg_src_b64, filter_clmsg_user_b64, filter_clmsg_body_b64, filter_clmsg_tag_b64,
153
- filter_svmsg_id_b64, filter_svmsg_sdate_b64, filter_svmsg_edate_b64,
172
+ filter_clmsg_src_b64, filter_clmsg_title_b64, filter_clmsg_user_b64, filter_clmsg_body_b64,
173
+ filter_clmsg_tag_b64, filter_svmsg_id_b64, filter_svmsg_sdate_b64, filter_svmsg_edate_b64,
154
174
  pg_enabled, pg_host_b64, pg_port, pg_user_b64, pg_password_b64, pg_dbname_b64],
155
175
  retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout)
156
- common.print_format(ret, args.format, tm, None, False, pf=pf)
176
+ common.print_format(ret, getattr(args, 'format', False), tm, None, False, pf=pf)
157
177
 
158
178
  if 'success' not in ret:
159
179
  return 1, ret, cl
@@ -196,39 +216,44 @@ class AuditSearch(audit_base.AuditBase):
196
216
  int: 終了コード
197
217
  """
198
218
  select = json.loads(convert.b64str2str(msg[2]))
199
- sort = json.loads(convert.b64str2str(msg[3]))
200
- offset = int(msg[4]) if msg[4] else 0
201
- limit = int(msg[5]) if msg[5] else 100
219
+ select_date_format = convert.b64str2str(msg[3])
220
+ groupby = json.loads(convert.b64str2str(msg[4]))
221
+ groupby_date_format = convert.b64str2str(msg[5])
222
+ sort = json.loads(convert.b64str2str(msg[6]))
223
+ offset = int(msg[7]) if msg[7] else 0
224
+ limit = int(msg[8]) if msg[8] else 100
202
225
 
203
- filter_audit_type = convert.b64str2str(msg[6])
204
- filter_clmsg_id = convert.b64str2str(msg[7])
205
- filter_clmsg_sdate = convert.b64str2str(msg[8])
206
- filter_clmsg_edate = convert.b64str2str(msg[9])
207
- filter_clmsg_src = convert.b64str2str(msg[10])
208
- filter_clmsg_user = convert.b64str2str(msg[11])
209
- body = json.loads(convert.b64str2str(msg[12]))
210
- tags = json.loads(convert.b64str2str(msg[13]))
211
- filter_svmsg_id = convert.b64str2str(msg[14])
212
- filter_svmsg_sdate = convert.b64str2str(msg[15])
213
- filter_svmsg_edate = convert.b64str2str(msg[16])
214
- pg_enabled = True if msg[17]=='True' else False
215
- pg_host = convert.b64str2str(msg[18])
216
- pg_port = int(msg[19]) if msg[19]!='None' else None
217
- pg_user = convert.b64str2str(msg[20])
218
- pg_password = convert.b64str2str(msg[21])
219
- pg_dbname = convert.b64str2str(msg[22])
220
- st = self.search(msg[1], select, sort, offset, limit,
226
+ filter_audit_type = convert.b64str2str(msg[9])
227
+ filter_clmsg_id = convert.b64str2str(msg[10])
228
+ filter_clmsg_sdate = convert.b64str2str(msg[11])
229
+ filter_clmsg_edate = convert.b64str2str(msg[12])
230
+ filter_clmsg_src = convert.b64str2str(msg[13])
231
+ filter_clmsg_title = convert.b64str2str(msg[14])
232
+ filter_clmsg_user = convert.b64str2str(msg[15])
233
+ body = json.loads(convert.b64str2str(msg[16]))
234
+ tags = json.loads(convert.b64str2str(msg[17]))
235
+ filter_svmsg_id = convert.b64str2str(msg[18])
236
+ filter_svmsg_sdate = convert.b64str2str(msg[19])
237
+ filter_svmsg_edate = convert.b64str2str(msg[20])
238
+ pg_enabled = True if msg[21]=='True' else False
239
+ pg_host = convert.b64str2str(msg[22])
240
+ pg_port = int(msg[23]) if msg[23]!='None' else None
241
+ pg_user = convert.b64str2str(msg[24])
242
+ pg_password = convert.b64str2str(msg[25])
243
+ pg_dbname = convert.b64str2str(msg[26])
244
+ st = self.search(msg[1], select, select_date_format, groupby, groupby_date_format,
245
+ sort, offset, limit,
221
246
  filter_audit_type, filter_clmsg_id, filter_clmsg_sdate, filter_clmsg_edate,
222
- filter_clmsg_src, filter_clmsg_user, body, tags,
247
+ filter_clmsg_src, filter_clmsg_title, filter_clmsg_user, body, tags,
223
248
  filter_svmsg_id, filter_svmsg_sdate, filter_svmsg_edate,
224
249
  pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname,
225
250
  data_dir, logger, redis_cli)
226
251
  return st
227
252
 
228
- def search(self, reskey:str, select:List[str], sort:Dict[str, str], offset:int, limit:int,
253
+ def search(self, reskey:str, select:Dict[str, str], select_date_format:str, groupby:List[str], groupby_date_format:str, sort:Dict[str, str], offset:int, limit:int,
229
254
  filter_audit_type:str, filter_clmsg_id:str, filter_clmsg_sdate:str, filter_clmsg_edate:str,
230
- filter_clmsg_src:str, filter_clmsg_user:str, filter_clmsg_body:Dict[str, Any], filter_clmsg_tags:List[str],
231
- filter_svmsg_id:str, filter_svmsg_sdate:str, filter_svmsg_edate:str,
255
+ filter_clmsg_src:str, filter_clmsg_title:str, filter_clmsg_user:str, filter_clmsg_body:Dict[str, Any],
256
+ filter_clmsg_tags:List[str], filter_svmsg_id:str, filter_svmsg_sdate:str, filter_svmsg_edate:str,
232
257
  pg_enabled:bool, pg_host:str, pg_port:int, pg_user:str, pg_password:str, pg_dbname:str,
233
258
  data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient) -> int:
234
259
  """
@@ -236,7 +261,10 @@ class AuditSearch(audit_base.AuditBase):
236
261
 
237
262
  Args:
238
263
  reskey (str): レスポンスキー
239
- select (List[str]): 取得項目
264
+ select (Dict[str, str]): 取得項目
265
+ select_date_format (str): 取得項目の日時フォーマット
266
+ groupby (List[str]): グループ化項目
267
+ groupby_date_format (str): グループ化項目の日時フォーマット
240
268
  sort (Dict[str, str]): ソート条件
241
269
  offset (int): 取得する行の開始位置
242
270
  limit (int): 取得する行数
@@ -245,6 +273,7 @@ class AuditSearch(audit_base.AuditBase):
245
273
  filter_clmsg_sdate (str): クライアントメッセージ発生日時(開始)
246
274
  filter_clmsg_edate (str): クライアントメッセージ発生日時(終了)
247
275
  filter_clmsg_src (str): クライアントメッセージの発生源
276
+ filter_clmsg_title (str): クライアントメッセージのタイトル
248
277
  filter_clmsg_user (str): クライアントメッセージの発生させたユーザー
249
278
  filter_clmsg_body (Dict[str, Any]): クライアントメッセージの本文
250
279
  filter_clmsg_tags (List[str]): クライアントメッセージのタグ
@@ -264,6 +293,31 @@ class AuditSearch(audit_base.AuditBase):
264
293
  Returns:
265
294
  int: レスポンスコード
266
295
  """
296
+ def _date_format(pg_enabled, col, date_format):
297
+ if col not in ['clmsg_date', 'svmsg_date']:
298
+ return col
299
+ if pg_enabled:
300
+ if date_format == '%u':
301
+ return f"to_char({col}, 'D')"
302
+ elif date_format == '%m':
303
+ return f"to_char({col}, 'MM')"
304
+ elif date_format == '%Y':
305
+ return f"to_char({col}, 'YYYY')"
306
+ elif date_format == '%Y/%m':
307
+ return f"to_char({col}, 'YYYY/MM')"
308
+ elif date_format == '%Y/%m/%d':
309
+ return f"to_char({col}, 'YYYY/MM/DD')"
310
+ elif date_format == '%Y/%m/%d %H':
311
+ return f"to_char({col}, 'YYYY/MM/DD HH24')"
312
+ elif date_format == '%Y/%m/%d %H:%M':
313
+ return f"to_char({col}, 'YYYY/MM/DD HH24:MI')"
314
+ else:
315
+ return col
316
+ else:
317
+ if date_format is not None and date_format != '':
318
+ return f"strftime('{date_format}', {col})"
319
+ else:
320
+ return col
267
321
  try:
268
322
  with self.initdb(data_dir, logger, pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname) as conn:
269
323
  def dict_factory(cursor, row):
@@ -271,17 +325,24 @@ class AuditSearch(audit_base.AuditBase):
271
325
  conn.row_factory = dict_row if pg_enabled else dict_factory
272
326
  cursor = conn.cursor()
273
327
  try:
274
- select = select if select else ['audit_type', 'clmsg_id', 'clmsg_date', 'clmsg_src', 'clmsg_user', 'clmsg_body', 'clmsg_tag', 'svmsg_id', 'svmsg_date']
328
+ select = {k:v for k,v in select.items() if k != ''} if select else None
329
+ select = select if select and len(select)>0 else {k:'-' for k in self.TBL_COLS}
275
330
  if pg_enabled:
276
331
  toz = common.get_tzoffset_str()
277
- sel = []
278
- for s in select:
279
- if s in ['clmsg_date', 'svmsg_date']:
280
- sel.append(f"{s} AT TIME ZONE INTERVAL '{toz}' as {s}")
332
+ sel = {}
333
+ for k,v in select.items():
334
+ if k in ['clmsg_date', 'svmsg_date']:
335
+ sel[f"{k} AT TIME ZONE INTERVAL '{toz}' as {k}"] = v
281
336
  else:
282
- sel.append(s)
337
+ sel[k] = v
283
338
  select = sel
284
- sql = f'SELECT {",".join(select)} FROM audit'
339
+ sql = []
340
+ for k,v in select.items():
341
+ if v in ['count', 'sum', 'avg', 'min', 'max']:
342
+ sql.append(f'{v}({_date_format(pg_enabled, k, select_date_format)}) AS {k}')
343
+ else:
344
+ sql.append(f'{_date_format(pg_enabled, k, select_date_format)} AS {k}')
345
+ sql = f"SELECT {','.join(sql)} FROM audit"
285
346
  params = []
286
347
  where = []
287
348
  if filter_audit_type and filter_audit_type != 'None':
@@ -299,6 +360,9 @@ class AuditSearch(audit_base.AuditBase):
299
360
  if filter_clmsg_src and filter_clmsg_src != 'None':
300
361
  where.append(f'clmsg_src LIKE {"%s" if pg_enabled else "?"}')
301
362
  params.append(filter_clmsg_src)
363
+ if filter_clmsg_title and filter_clmsg_title != 'None':
364
+ where.append(f'clmsg_title LIKE {"%s" if pg_enabled else "?"}')
365
+ params.append(filter_clmsg_title)
302
366
  if filter_clmsg_user and filter_clmsg_user != 'None':
303
367
  where.append(f'clmsg_user LIKE {"%s" if pg_enabled else "?"}')
304
368
  params.append(filter_clmsg_user)
@@ -322,6 +386,8 @@ class AuditSearch(audit_base.AuditBase):
322
386
  where.append(f'svmsg_date<={"%s" if pg_enabled else "?"}')
323
387
  params.append(filter_svmsg_edate)
324
388
  sql += ' WHERE ' + ' AND '.join(where) if len(where)>0 else ''
389
+ if groupby and len(groupby) > 0:
390
+ sql += ' GROUP BY ' + ', '.join([f"{_date_format(pg_enabled, g, groupby_date_format)}" for g in groupby])
325
391
  if sort and len(sort) > 0:
326
392
  sql += ' ORDER BY ' + ', '.join([f"{k} {v}" for k, v in sort.items()])
327
393
  else:
@@ -53,6 +53,9 @@ class AuditWrite(audit_base.AuditBase):
53
53
  dict(opt="clmsg_src", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
54
54
  discription_ja="クライアントのメッセージの発生源を指定します。通常 `cmdbox.app.feature.Feature` を継承したクラス名を指定します。",
55
55
  discription_en="Specifies the source of client messages. Usually specifies the name of a class that extends `cmdbox.app.feature.Feature` ."),
56
+ dict(opt="clmsg_title", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
57
+ discription_ja="クライアントのメッセージタイトルを指定します。通常コマンドタイトルを指定します。",
58
+ discription_en="Specifies the client message title. Usually specifies the command title."),
56
59
  dict(opt="clmsg_user", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
57
60
  discription_ja="クライアントのメッセージを発生させたユーザーを指定します。",
58
61
  discription_en="SpecSpecifies the user who generated the client message."),
@@ -107,6 +110,7 @@ class AuditWrite(audit_base.AuditBase):
107
110
  clmsg_id_b64 = convert.str2b64str(args.clmsg_id)
108
111
  clmsg_date_b64 = convert.str2b64str(args.clmsg_date)
109
112
  clmsg_src_b64 = convert.str2b64str(args.clmsg_src) if args.clmsg_src is not None else ''
113
+ clmsg_title_b64 = convert.str2b64str(args.clmsg_title) if args.clmsg_title is not None else ''
110
114
  clmsg_user_b64 = convert.str2b64str(args.clmsg_user) if args.clmsg_user is not None else ''
111
115
  clmsg_body_str = json.dumps(args.clmsg_body, default=common.default_json_enc, ensure_ascii=False) if args.clmsg_body is not None else '{}'
112
116
  clmsg_body_b64 = convert.str2b64str(clmsg_body_str)
@@ -121,7 +125,7 @@ class AuditWrite(audit_base.AuditBase):
121
125
 
122
126
  cl = client.Client(logger, redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname)
123
127
  cl.redis_cli.send_cmd(self.get_svcmd(),
124
- [audit_type_b64, clmsg_id_b64, clmsg_date_b64, clmsg_src_b64, clmsg_user_b64, clmsg_body_b64, clmsg_tag_b64,
128
+ [audit_type_b64, clmsg_id_b64, clmsg_date_b64, clmsg_src_b64, clmsg_title_b64, clmsg_user_b64, clmsg_body_b64, clmsg_tag_b64,
125
129
  pg_enabled, pg_host_b64, pg_port, pg_user_b64, pg_password_b64, pg_dbname_b64,
126
130
  args.retention_period_days],
127
131
  retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout, nowait=True)
@@ -157,24 +161,26 @@ class AuditWrite(audit_base.AuditBase):
157
161
  clmsg_id = convert.b64str2str(msg[3])
158
162
  clmsg_date = convert.b64str2str(msg[4])
159
163
  clmsg_src = convert.b64str2str(msg[5])
160
- clmsg_user = convert.b64str2str(msg[6])
161
- clmsg_body = convert.b64str2str(msg[7])
162
- clmsg_tags = convert.b64str2str(msg[8])
163
- pg_enabled = True if msg[9]=='True' else False
164
- pg_host = convert.b64str2str(msg[10])
165
- pg_port = int(msg[11]) if msg[11]!='None' else None
166
- pg_user = convert.b64str2str(msg[12])
167
- pg_password = convert.b64str2str(msg[13])
168
- pg_dbname = convert.b64str2str(msg[14])
169
- retention_period_days = int(msg[15]) if msg[15] != 'None' else None
164
+ clmsg_title = convert.b64str2str(msg[6])
165
+ clmsg_user = convert.b64str2str(msg[7])
166
+ clmsg_body = convert.b64str2str(msg[8])
167
+ clmsg_tags = convert.b64str2str(msg[9])
168
+ pg_enabled = True if msg[10]=='True' else False
169
+ pg_host = convert.b64str2str(msg[11])
170
+ pg_port = int(msg[12]) if msg[12]!='None' else None
171
+ pg_user = convert.b64str2str(msg[13])
172
+ pg_password = convert.b64str2str(msg[14])
173
+ pg_dbname = convert.b64str2str(msg[15])
174
+ retention_period_days = int(msg[16]) if msg[16] != 'None' else None
170
175
  svmsg_id = str(uuid.uuid4())
171
- st = self.write(msg[1], audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_user, clmsg_body, clmsg_tags, svmsg_id,
176
+ st = self.write(msg[1], audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id,
172
177
  pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname,
173
178
  retention_period_days,
174
179
  data_dir, logger, redis_cli)
175
180
  return st
176
181
 
177
- def write(self, reskey:str, audit_type:str, clmsg_id:str, clmsg_date:str, clmsg_src:str, clmsg_user:str, clmsg_body:str, clmsg_tags:str, svmsg_id:str,
182
+ def write(self, reskey:str, audit_type:str, clmsg_id:str, clmsg_date:str, clmsg_src:str, clmsg_title:str,
183
+ clmsg_user:str, clmsg_body:str, clmsg_tags:str, svmsg_id:str,
178
184
  pg_enabled:bool, pg_host:str, pg_port:int, pg_user:str, pg_password:str, pg_dbname:str,
179
185
  retention_period_days:int,
180
186
  data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient) -> int:
@@ -187,6 +193,7 @@ class AuditWrite(audit_base.AuditBase):
187
193
  clmsg_id (str): クライアントメッセージID
188
194
  clmsg_date (str): クライアントメッセージ発生日時
189
195
  clmsg_src (str): クライアントメッセージの発生源
196
+ clmsg_title (str): クライアントメッセージのタイトル
190
197
  clmsg_user (str): クライアントメッセージの発生させたユーザー
191
198
  clmsg_body (str): クライアントメッセージの本文
192
199
  clmsg_tags (str): クライアントメッセージのタグ
@@ -209,22 +216,22 @@ class AuditWrite(audit_base.AuditBase):
209
216
  with self.initdb(data_dir, logger, pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname) as conn:
210
217
  cursor = conn.cursor()
211
218
  try:
212
- clmsg_tags = json.dumps(clmsg_tags)
219
+ svmsg_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + common.get_tzoffset_str()
213
220
  if not pg_enabled:
214
221
  cursor.execute('''
215
- INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_user, clmsg_body, clmsg_tag,
222
+ INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tag,
216
223
  svmsg_id, svmsg_date)
217
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
218
- ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_user, clmsg_body, clmsg_tags, svmsg_id))
224
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
225
+ ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id, svmsg_date))
219
226
  if retention_period_days is not None and retention_period_days > 0:
220
227
  cursor.execute('DELETE FROM audit WHERE svmsg_date < datetime(CURRENT_TIMESTAMP, ?)',
221
228
  (f'-{retention_period_days} days',))
222
229
  else:
223
230
  cursor.execute('''
224
- INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_user, clmsg_body, clmsg_tag,
231
+ INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tag,
225
232
  svmsg_id, svmsg_date)
226
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, CURRENT_TIMESTAMP)
227
- ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_user, clmsg_body, clmsg_tags, svmsg_id))
233
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
234
+ ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id, svmsg_date))
228
235
  if retention_period_days is not None and retention_period_days > 0:
229
236
  cursor.execute("DELETE FROM audit WHERE svmsg_date < CURRENT_TIMESTAMP + %s ",
230
237
  (f'-{retention_period_days} day',))
@@ -0,0 +1,81 @@
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
+ raise dict(error='audit search feature is not found.')
45
+ opt = await req.json()
46
+ opt = {**opt, **web.options.audit_search_args.copy()}
47
+ args = argparse.Namespace(**{k:common.chopdq(v) for k,v in opt.items()})
48
+ status, ret_main, _ = web.options.audit_search.apprun(web.logger, args, time.perf_counter(), [])
49
+ if status != 0:
50
+ return dict(error=ret_main)
51
+ return ret_main
52
+
53
+ @app.get('/audit/mode_cmd')
54
+ async def audit_mode_cmd(req:Request, res:Response):
55
+ signin = web.signin.check_signin(req, res)
56
+ if signin is not None:
57
+ return signin
58
+ return dict(success=web.options.audit_search_args)
59
+
60
+ def toolmenu(self, web:Web) -> Dict[str, Any]:
61
+ """
62
+ ツールメニューの情報を返します
63
+
64
+ Args:
65
+ web (Web): Webオブジェクト
66
+
67
+ Returns:
68
+ Dict[str, Any]: ツールメニュー情報
69
+
70
+ Sample:
71
+ {
72
+ 'filer': {
73
+ 'html': 'Filer',
74
+ 'href': 'filer',
75
+ 'target': '_blank',
76
+ 'css_class': 'dropdown-item'
77
+ 'onclick': 'alert("filer")'
78
+ }
79
+ }
80
+ """
81
+ return dict(audit=dict(html='Audit', href='audit', target='_blank', css_class='dropdown-item'))