nginx-lens 0.1.5__py3-none-any.whl → 0.1.6__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.
commands/analyze.py CHANGED
@@ -58,7 +58,14 @@ def analyze(config_path: str = typer.Argument(..., help="Путь к nginx.conf"
58
58
  Пример:
59
59
  nginx-lens analyze /etc/nginx/nginx.conf
60
60
  """
61
- tree = parse_nginx_config(config_path)
61
+ try:
62
+ tree = parse_nginx_config(config_path)
63
+ except FileNotFoundError:
64
+ console.print(f"[red]Файл {config_path} не найден. Проверьте путь к конфигу.[/red]")
65
+ return
66
+ except Exception as e:
67
+ console.print(f"[red]Ошибка при разборе {config_path}: {e}[/red]")
68
+ return
62
69
  conflicts = find_location_conflicts(tree)
63
70
  dups = find_duplicate_directives(tree)
64
71
  empties = find_empty_blocks(tree)
commands/diff.py CHANGED
@@ -3,6 +3,7 @@ from rich.console import Console
3
3
  from rich.table import Table
4
4
  from analyzer.diff import diff_trees
5
5
  from parser.nginx_parser import parse_nginx_config
6
+ import difflib
6
7
 
7
8
  app = typer.Typer()
8
9
  console = Console()
@@ -12,28 +13,39 @@ def diff(
12
13
  config2: str = typer.Argument(..., help="Второй nginx.conf")
13
14
  ):
14
15
  """
15
- Сравнивает две конфигурации Nginx и выводит отличия side-by-side.
16
+ Сравнивает две конфигурации Nginx и выводит отличия построчно side-by-side с подсветкой.
16
17
 
17
18
  Пример:
18
19
  nginx-lens diff /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
19
20
  """
20
- tree1 = parse_nginx_config(config1)
21
- tree2 = parse_nginx_config(config2)
22
- diffs = diff_trees(tree1, tree2)
23
- if not diffs:
24
- console.print("[green]Конфигурации идентичны[/green]")
21
+ try:
22
+ with open(config1) as f1, open(config2) as f2:
23
+ lines1 = f1.readlines()
24
+ lines2 = f2.readlines()
25
+ except FileNotFoundError as e:
26
+ console.print(f"[red]Файл {e.filename} не найден. Проверьте путь к конфигу.[/red]")
25
27
  return
28
+ except Exception as e:
29
+ console.print(f"[red]Ошибка при чтении файлов: {e}[/red]")
30
+ return
31
+ maxlen = max(len(lines1), len(lines2))
32
+ # Выравниваем длины
33
+ lines1 += [''] * (maxlen - len(lines1))
34
+ lines2 += [''] * (maxlen - len(lines2))
26
35
  table = Table(show_header=True, header_style="bold blue")
27
- table.add_column("Config 1", style="red")
28
- table.add_column("Config 2", style="green")
29
- for d in diffs:
30
- path = "/".join(d['path'])
31
- if d['type'] == 'added':
32
- table.add_row("", f"+ {path}")
33
- elif d['type'] == 'removed':
34
- table.add_row(f"- {path}", "")
35
- elif d['type'] == 'changed':
36
- v1 = str(d['value1'])
37
- v2 = str(d['value2'])
38
- table.add_row(f"! {path}\n{v1}", f"! {path}\n{v2}")
36
+ table.add_column("", style="dim", width=4)
37
+ table.add_column("Config 1", style="white")
38
+ table.add_column("№", style="dim", width=4)
39
+ table.add_column("Config 2", style="white")
40
+ for i in range(maxlen):
41
+ l1 = lines1[i].rstrip('\n')
42
+ l2 = lines2[i].rstrip('\n')
43
+ n1 = str(i+1) if l1 else ''
44
+ n2 = str(i+1) if l2 else ''
45
+ if l1 == l2:
46
+ table.add_row(n1, l1, n2, l2)
47
+ else:
48
+ style1 = "red" if l1 else "on red"
49
+ style2 = "green" if l2 else "on green"
50
+ table.add_row(f"[bold]{n1}[/bold]" if l1 else n1, f"[{style1}]{l1}[/{style1}]" if l1 or l2 else '', f"[bold]{n2}[/bold]" if l2 else n2, f"[{style2}]{l2}[/{style2}]" if l1 or l2 else '')
39
51
  console.print(table)
commands/graph.py CHANGED
@@ -59,17 +59,61 @@ def graph(
59
59
  console.print("[yellow]Не найдено ни одного маршрута[/yellow]")
60
60
  return
61
61
  # Красивый вывод
62
+ seen = set()
62
63
  for route in routes:
64
+ if not route or route[0][0] != 'server':
65
+ continue
66
+ key = tuple(route)
67
+ if key in seen:
68
+ continue
69
+ seen.add(key)
63
70
  t = Text()
64
- for i, (typ, val) in enumerate(route):
65
- if typ == 'server':
66
- t.append(f"server: {val}", style="bold blue")
67
- elif typ == 'location':
68
- t.append(f" -> location: {val}", style="yellow")
71
+ # Получаем label для server
72
+ server_val = route[0][1]
73
+ server_block = None
74
+ # Найти сам блок server по arg
75
+ for d in tree.directives:
76
+ if d.get('block') == 'server' and (d.get('arg','') == server_val or (not d.get('arg') and (server_val == '[no arg]' or not server_val))):
77
+ server_block = d
78
+ break
79
+ label = get_server_label(server_block) if server_block else (server_val or 'default')
80
+ t.append(f"[", style="white")
81
+ t.append(f"server: {label}", style="bold blue")
82
+ t.append("]", style="white")
83
+ for i, (typ, val) in enumerate(route[1:]):
84
+ if typ == 'location':
85
+ t.append(f" -> [", style="white")
86
+ t.append(f"location: {val}", style="yellow")
87
+ t.append("]", style="white")
69
88
  elif typ == 'proxy_pass':
70
89
  t.append(f" -> proxy_pass: {val}", style="green")
71
90
  elif typ == 'upstream':
72
- t.append(f" -> upstream: {val}", style="magenta")
91
+ t.append(f" -> [", style="white")
92
+ t.append(f"upstream: {val}", style="magenta")
93
+ t.append("]", style="white")
73
94
  elif typ == 'upstream_server':
74
- t.append(f" -> server: {val}", style="grey50")
75
- console.print(t)
95
+ t.append(f" -> [", style="white")
96
+ t.append(f"server: {val}", style="grey50")
97
+ t.append("]", style="white")
98
+ console.print(t)
99
+
100
+ def get_server_label(server_block):
101
+ arg = server_block.get('arg')
102
+ if arg and arg != '[no arg]':
103
+ return arg
104
+ # Ищем server_name
105
+ names = []
106
+ listens = []
107
+ for sub in server_block.get('directives', []):
108
+ if sub.get('directive') == 'server_name':
109
+ names += sub.get('args', '').split()
110
+ if sub.get('directive') == 'listen':
111
+ listens.append(sub.get('args', ''))
112
+ if names:
113
+ return ' '.join(names)
114
+ if listens:
115
+ return ','.join(listens)
116
+ return 'default'
117
+
118
+ if __name__ == "__main__":
119
+ app()
commands/include.py CHANGED
@@ -18,7 +18,14 @@ def include_tree(
18
18
  nginx-lens include-tree /etc/nginx/nginx.conf
19
19
  nginx-lens include-tree /etc/nginx/nginx.conf --directive server_name
20
20
  """
21
- tree = build_include_tree(config_path)
21
+ try:
22
+ tree = build_include_tree(config_path)
23
+ except FileNotFoundError:
24
+ console.print(f"[red]Файл {config_path} не найден. Проверьте путь к конфигу.[/red]")
25
+ return
26
+ except Exception as e:
27
+ console.print(f"[red]Ошибка при разборе {config_path}: {e}[/red]")
28
+ return
22
29
  rich_tree = Tree(f"[bold blue]{config_path}[/bold blue]")
23
30
  def _add(node, t):
24
31
  for k, v in t.items():
commands/route.py CHANGED
@@ -31,8 +31,12 @@ def route(
31
31
  for conf in configs:
32
32
  try:
33
33
  tree = parse_nginx_config(conf)
34
+ except FileNotFoundError:
35
+ console.print(f"[red]Файл {conf} не найден. Проверьте путь к конфигу.[/red]")
36
+ continue
34
37
  except Exception as e:
35
- continue # пропускаем битые/невалидные
38
+ console.print(f"[red]Ошибка при разборе {conf}: {e}[/red]")
39
+ continue
36
40
  res = find_route(tree, url)
37
41
  if res:
38
42
  server = res['server']
commands/syntax.py CHANGED
@@ -33,6 +33,9 @@ def syntax(
33
33
  if not config_path:
34
34
  console.print("[red]Не удалось найти nginx.conf. Укажите путь через -c.[/red]")
35
35
  return
36
+ if not os.path.isfile(config_path):
37
+ console.print(f"[red]Файл {config_path} не найден. Проверьте путь к конфигу.[/red]")
38
+ return
36
39
  cmd = [nginx_path, "-t", "-c", os.path.abspath(config_path)]
37
40
  try:
38
41
  result = subprocess.run(cmd, capture_output=True, text=True, check=False)
@@ -41,8 +44,8 @@ def syntax(
41
44
  return
42
45
  else:
43
46
  console.print("[red]Ошибка синтаксиса![/red]")
44
- console.print(result.stdout)
45
- console.print(result.stderr)
47
+ console.print(result.stdout)
48
+ console.print(result.stderr)
46
49
  # Парсим все ошибки
47
50
  err = result.stderr or result.stdout
48
51
  errors = list(ERRORS_RE.finditer(err))
commands/tree.py CHANGED
@@ -34,7 +34,14 @@ def tree(
34
34
  nginx-lens tree /etc/nginx/nginx.conf --markdown
35
35
  nginx-lens tree /etc/nginx/nginx.conf --html
36
36
  """
37
- tree_obj = parse_nginx_config(config_path)
37
+ try:
38
+ tree_obj = parse_nginx_config(config_path)
39
+ except FileNotFoundError:
40
+ console.print(f"[red]Файл {config_path} не найден. Проверьте путь к конфигу.[/red]")
41
+ return
42
+ except Exception as e:
43
+ console.print(f"[red]Ошибка при разборе {config_path}: {e}[/red]")
44
+ return
38
45
  root = RichTree(f"[bold blue]nginx.conf[/bold blue]")
39
46
  _build_tree(tree_obj.directives, root)
40
47
  if markdown:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nginx-lens
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: CLI-инструмент для анализа, визуализации и диагностики конфигураций Nginx
5
5
  Author: Daniil Astrouski
6
6
  Author-email: shelovesuastra@gmail.com
@@ -11,16 +11,16 @@ analyzer/route.py,sha256=2xxQooQEsfn10tzGCZUoP32T0OnTMnPB6qRgBR6not8,2345
11
11
  analyzer/unused.py,sha256=Ixzv0bPsw9IafblVwLiAOgugdg2dGu1MJDtuoqzPZiY,1066
12
12
  analyzer/warnings.py,sha256=zC36QMvegA2eQPvZ-P1eysrX_kXHx5A1MUKHKKNvG5c,5784
13
13
  commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- commands/analyze.py,sha256=QmsyeIlGqvhuRqlUFpPrzg90X5yDVJ3BSIGPhkCHYgI,8026
14
+ commands/analyze.py,sha256=W6begSgXNjgKJGoGeguR3WKgHPLkClWXxxpDcqvsJdc,8343
15
15
  commands/cli.py,sha256=9HwDJ-po5al0ceb4Wkyw5F2wzqxbJTo0CbHQ2AQ8obo,722
16
- commands/diff.py,sha256=VeZsUu1BYrDubDFrmM5xC4DUWZvWUjFRHfddXm3-I3c,1373
17
- commands/graph.py,sha256=MkPasTYSnpG4i3ge_l36t0YUawE3e71v4RWZF9BlJ8A,3084
16
+ commands/diff.py,sha256=C7gRIWh6DNWHzjiQBPVTn-rZ40m2KCY75Zd6Q4URJIE,2076
17
+ commands/graph.py,sha256=fvYDUvj_BXdIAm8eg52v5VxhkZijb2YDkCw7D21ZK-M,4650
18
18
  commands/health.py,sha256=d2bBui0qauQtO4Ll9cjniKR1Y5dYJBQzG9CECDnsUQ4,1365
19
- commands/include.py,sha256=zPqJpbbSU_6S3L4ntFncPmyFWba8smvdCRog_SFuAFI,1907
19
+ commands/include.py,sha256=5PTYG5C00-AlWfIgpQXLq9E7C9yTFSv7HrZkM5ogDps,2224
20
20
  commands/logs.py,sha256=8tnGsgNy_B97S3O0D_6bvOVfNvAyqeUNotlDOdjltgw,3420
21
- commands/route.py,sha256=MpSI88lqiqkjwA4q-P7gUXmkDicJXYonTwFVC6fNs44,2012
22
- commands/syntax.py,sha256=iy5JHtqLWs4OtNYuM1xDESlfJ9tOJ4RdNi1KDTfEO1k,3147
23
- commands/tree.py,sha256=NEhNU66_e0JCsD4xh4315TM-xwo8NkPwc00lZ4saPzE,1844
21
+ commands/route.py,sha256=H_xMk9KoqwesutzT9ewiTsakfdjmu01kka9ZQc14faY,2222
22
+ commands/syntax.py,sha256=gABzFvml92bHqYa6YdRx78bmrtd9v2ORT_Hz-gfQu1U,3336
23
+ commands/tree.py,sha256=mDfx0Aeg1EDQSYQoJ2nJIkSd_uP7ZR7pEqy7Cw3clQ0,2161
24
24
  exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  exporter/graph.py,sha256=WYUrqUgCaK6KihgxAcRHaQn4oMo6b7ybC8yb_36ZIsA,3995
26
26
  exporter/html.py,sha256=uquEM-WvBt2aV9GshgaI3UVhYd8sD0QQ-OmuNtvYUdU,798
@@ -29,8 +29,8 @@ parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  parser/nginx_parser.py,sha256=JqZ3clNy4Nf-bmbsx_rJUL7EgRoB79b87eEu_isMeqg,3577
30
30
  upstream_checker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  upstream_checker/checker.py,sha256=9-6CMUTN7gXUACP8EwX722QogfujZyV-WWWUeM3a79k,455
32
- nginx_lens-0.1.5.dist-info/METADATA,sha256=D8emGIWms0maTGCxi5cKe_RMQ9a1veunrpre0tNJUzo,476
33
- nginx_lens-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
- nginx_lens-0.1.5.dist-info/entry_points.txt,sha256=qEcecjSyLqcJjbIVlNlTpqAhPqDyaujUV5ZcBTAr3po,48
35
- nginx_lens-0.1.5.dist-info/top_level.txt,sha256=mxLJO4rZg0rbixVGhplF3fUNFs8vxDIL25ronZNvRy4,51
36
- nginx_lens-0.1.5.dist-info/RECORD,,
32
+ nginx_lens-0.1.6.dist-info/METADATA,sha256=FhO8QQOa9YfJzMUtNq313YcaNQAhHr2UEMNHXDIJkBU,476
33
+ nginx_lens-0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ nginx_lens-0.1.6.dist-info/entry_points.txt,sha256=qEcecjSyLqcJjbIVlNlTpqAhPqDyaujUV5ZcBTAr3po,48
35
+ nginx_lens-0.1.6.dist-info/top_level.txt,sha256=mxLJO4rZg0rbixVGhplF3fUNFs8vxDIL25ronZNvRy4,51
36
+ nginx_lens-0.1.6.dist-info/RECORD,,