ndev-stack 0.1.0__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.
- ndev/__init__.py +8 -0
- ndev/__main__.py +4 -0
- ndev/cli.py +24 -0
- ndev/common/__init__.py +3 -0
- ndev/common/config.py +114 -0
- ndev/common/constants.py +51 -0
- ndev/common/github.py +13 -0
- ndev/common/logger.py +11 -0
- ndev/common/manifest.py +41 -0
- ndev/common/utils.py +96 -0
- ndev/linux/__init__.py +1 -0
- ndev/linux/chroot/manager.py +63 -0
- ndev/linux/chroot/packages.py +91 -0
- ndev/linux/chroot/shell.py +9 -0
- ndev/linux/cli.py +235 -0
- ndev/linux/commands/available.py +38 -0
- ndev/linux/commands/clean.py +25 -0
- ndev/linux/commands/ctl.py +192 -0
- ndev/linux/commands/current.py +11 -0
- ndev/linux/commands/db.py +319 -0
- ndev/linux/commands/doctor.py +56 -0
- ndev/linux/commands/grok.py +75 -0
- ndev/linux/commands/install.py +39 -0
- ndev/linux/commands/list.py +47 -0
- ndev/linux/commands/logs.py +36 -0
- ndev/linux/commands/mailpit.py +82 -0
- ndev/linux/commands/reload.py +26 -0
- ndev/linux/commands/restart.py +34 -0
- ndev/linux/commands/setup.py +113 -0
- ndev/linux/commands/start.py +34 -0
- ndev/linux/commands/status.py +81 -0
- ndev/linux/commands/stop.py +34 -0
- ndev/linux/commands/uninstall.py +69 -0
- ndev/linux/commands/update.py +57 -0
- ndev/linux/commands/upgrade.py +81 -0
- ndev/linux/commands/use.py +108 -0
- ndev/linux/commands/vhost.py +350 -0
- ndev/linux/php/builder.py +183 -0
- ndev/linux/php/downloader.py +58 -0
- ndev/linux/php/extensions.py +146 -0
- ndev/linux/php/installer.py +42 -0
- ndev/linux/php/resolver.py +59 -0
- ndev/linux/php/templates.py +128 -0
- ndev/linux/runtime/fpm.py +117 -0
- ndev/linux/runtime/mailpit.py +244 -0
- ndev/linux/runtime/pma.py +223 -0
- ndev/linux/runtime/process.py +37 -0
- ndev/linux/runtime/sockets.py +16 -0
- ndev/linux/runtime/upgrade.py +431 -0
- ndev/linux/tui.py +1423 -0
- ndev/main.py +52 -0
- ndev/tui.py +23 -0
- ndev/win/__init__.py +1 -0
- ndev/win/cli.py +1898 -0
- ndev/win/commands/__init__.py +0 -0
- ndev/win/core/__init__.py +0 -0
- ndev/win/core/db.py +265 -0
- ndev/win/core/elevate.py +94 -0
- ndev/win/core/ext.py +241 -0
- ndev/win/core/fcgi.py +216 -0
- ndev/win/core/grok.py +55 -0
- ndev/win/core/logs.py +66 -0
- ndev/win/core/mailpit.py +236 -0
- ndev/win/core/mkcert.py +65 -0
- ndev/win/core/paths.py +85 -0
- ndev/win/core/php.py +533 -0
- ndev/win/core/pma.py +190 -0
- ndev/win/core/services.py +349 -0
- ndev/win/core/setup.py +361 -0
- ndev/win/core/upgrade.py +513 -0
- ndev/win/core/vhost.py +289 -0
- ndev/win/templates/vhost.conf.tmpl +33 -0
- ndev/win/templates/vhost_ssl.conf.tmpl +43 -0
- ndev/win/tui.py +1313 -0
- ndev_stack-0.1.0.dist-info/METADATA +553 -0
- ndev_stack-0.1.0.dist-info/RECORD +79 -0
- ndev_stack-0.1.0.dist-info/WHEEL +5 -0
- ndev_stack-0.1.0.dist-info/entry_points.txt +4 -0
- ndev_stack-0.1.0.dist-info/top_level.txt +1 -0
ndev/win/core/vhost.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Virtual host management for Windows:
|
|
3
|
+
- Writes Nginx server blocks under ~/.ndev/nginx/conf/ndev-vhosts/<domain>.conf
|
|
4
|
+
- Updates C:\\Windows\\System32\\drivers\\etc\\hosts (127.0.0.1)
|
|
5
|
+
- Generates local SSL certificates with mkcert
|
|
6
|
+
- Controls php-cgi FastCGI worker pool
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import base64
|
|
11
|
+
import re
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from . import fcgi, mkcert, paths
|
|
16
|
+
from .elevate import is_admin, run_elevated
|
|
17
|
+
|
|
18
|
+
TEMPLATE_PATH = Path(__file__).parent.parent / "templates" / "vhost.conf.tmpl"
|
|
19
|
+
SSL_TEMPLATE_PATH = Path(__file__).parent.parent / "templates" / "vhost_ssl.conf.tmpl"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _load_templates() -> tuple[str, str]:
|
|
23
|
+
user_tmpl = paths.TEMPLATES_DIR / "vhost.conf.tmpl"
|
|
24
|
+
user_ssl_tmpl = paths.TEMPLATES_DIR / "vhost_ssl.conf.tmpl"
|
|
25
|
+
|
|
26
|
+
tmpl = (
|
|
27
|
+
user_tmpl.read_text(encoding="utf-8")
|
|
28
|
+
if user_tmpl.exists()
|
|
29
|
+
else TEMPLATE_PATH.read_text(encoding="utf-8")
|
|
30
|
+
)
|
|
31
|
+
ssl_tmpl = (
|
|
32
|
+
user_ssl_tmpl.read_text(encoding="utf-8")
|
|
33
|
+
if user_ssl_tmpl.exists()
|
|
34
|
+
else SSL_TEMPLATE_PATH.read_text(encoding="utf-8")
|
|
35
|
+
)
|
|
36
|
+
return tmpl, ssl_tmpl
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def write_vhost_conf(domain: str, root: str | Path, php_version: str, ssl: bool = False) -> Path:
|
|
40
|
+
tmpl, ssl_tmpl = _load_templates()
|
|
41
|
+
|
|
42
|
+
upstream_name = fcgi.nginx_upstream_name(php_version, domain=domain)
|
|
43
|
+
upstream_block = fcgi.render_upstream_block(php_version, domain=domain)
|
|
44
|
+
|
|
45
|
+
root_clean = str(Path(root).resolve()).replace("\\", "/")
|
|
46
|
+
ndev_home = str(paths.NDEV_HOME.resolve()).replace("\\", "/")
|
|
47
|
+
|
|
48
|
+
# Ensure logs directory exists
|
|
49
|
+
paths.NGINX_LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
if ssl:
|
|
52
|
+
cert_path, key_path = mkcert.generate_cert(domain)
|
|
53
|
+
conf = ssl_tmpl.format(
|
|
54
|
+
domain=domain,
|
|
55
|
+
root=root_clean,
|
|
56
|
+
ndev_home=ndev_home,
|
|
57
|
+
upstream_name=upstream_name,
|
|
58
|
+
upstream_block=upstream_block,
|
|
59
|
+
cert_path=cert_path,
|
|
60
|
+
key_path=key_path,
|
|
61
|
+
)
|
|
62
|
+
else:
|
|
63
|
+
conf = tmpl.format(
|
|
64
|
+
domain=domain,
|
|
65
|
+
root=root_clean,
|
|
66
|
+
ndev_home=ndev_home,
|
|
67
|
+
upstream_name=upstream_name,
|
|
68
|
+
upstream_block=upstream_block,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
conf_header = f"# ndev-domain: {domain}\n# ndev-php: {php_version}\n"
|
|
72
|
+
full_conf = conf_header + conf
|
|
73
|
+
|
|
74
|
+
paths.NGINX_CONF_D.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
conf_path = paths.NGINX_CONF_D / f"{domain}.conf"
|
|
76
|
+
conf_path.write_text(full_conf, encoding="utf-8")
|
|
77
|
+
return conf_path
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _ensure_writable(path: Path) -> None:
|
|
81
|
+
if path.exists():
|
|
82
|
+
import stat
|
|
83
|
+
try:
|
|
84
|
+
mode = path.stat().st_mode
|
|
85
|
+
if not (mode & stat.S_IWRITE):
|
|
86
|
+
path.chmod(mode | stat.S_IWRITE)
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def add_hosts_entry(domain: str) -> bool:
|
|
92
|
+
"""
|
|
93
|
+
Add domain -> 127.0.0.1 mapping to Windows hosts file.
|
|
94
|
+
Uses direct write if admin, or elevated PowerShell if unprivileged.
|
|
95
|
+
"""
|
|
96
|
+
domain = domain.strip().lower()
|
|
97
|
+
text = ""
|
|
98
|
+
if paths.HOSTS_PATH.exists():
|
|
99
|
+
text = paths.HOSTS_PATH.read_text(encoding="utf-8", errors="ignore")
|
|
100
|
+
|
|
101
|
+
for line in text.splitlines():
|
|
102
|
+
line = line.strip()
|
|
103
|
+
if not line or line.startswith("#"):
|
|
104
|
+
continue
|
|
105
|
+
parts = line.split()
|
|
106
|
+
if len(parts) >= 2 and domain in [p.lower() for p in parts[1:]]:
|
|
107
|
+
return False # Already mapped
|
|
108
|
+
|
|
109
|
+
if is_admin():
|
|
110
|
+
_ensure_writable(paths.HOSTS_PATH)
|
|
111
|
+
with paths.HOSTS_PATH.open("a", encoding="utf-8") as f:
|
|
112
|
+
f.write(f"\n127.0.0.1\t{domain}\n")
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
entry_bytes = f"\r\n127.0.0.1\t{domain}\r\n".encode("utf-8")
|
|
116
|
+
b64_entry = base64.b64encode(entry_bytes).decode("ascii")
|
|
117
|
+
ps_script = (
|
|
118
|
+
f"Set-ItemProperty -Path '{paths.HOSTS_PATH}' -Name IsReadOnly -Value $false -ErrorAction SilentlyContinue; "
|
|
119
|
+
f"$bytes = [System.Convert]::FromBase64String('{b64_entry}'); "
|
|
120
|
+
f"$stream = [System.IO.File]::Open('{paths.HOSTS_PATH}', [System.IO.FileMode]::Append); "
|
|
121
|
+
f"$stream.Write($bytes, 0, $bytes.Length); $stream.Close()"
|
|
122
|
+
)
|
|
123
|
+
encoded = base64.b64encode(ps_script.encode("utf-16le")).decode("ascii")
|
|
124
|
+
exit_code = run_elevated(["powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded])
|
|
125
|
+
if exit_code != 0:
|
|
126
|
+
raise RuntimeError(f"Failed to update hosts file (exit code {exit_code})")
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def remove_hosts_entry(domain: str) -> bool:
|
|
131
|
+
"""Remove domain from Windows hosts file."""
|
|
132
|
+
domain = domain.strip().lower()
|
|
133
|
+
if not paths.HOSTS_PATH.exists():
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
text = paths.HOSTS_PATH.read_text(encoding="utf-8", errors="ignore")
|
|
137
|
+
lines = text.splitlines()
|
|
138
|
+
new_lines = []
|
|
139
|
+
removed = False
|
|
140
|
+
|
|
141
|
+
for line in lines:
|
|
142
|
+
stripped = line.strip()
|
|
143
|
+
if not stripped or stripped.startswith("#"):
|
|
144
|
+
new_lines.append(line)
|
|
145
|
+
continue
|
|
146
|
+
parts = stripped.split()
|
|
147
|
+
if len(parts) >= 2 and domain in [p.lower() for p in parts[1:]]:
|
|
148
|
+
remaining_hosts = [p for p in parts[1:] if p.lower() != domain]
|
|
149
|
+
if remaining_hosts:
|
|
150
|
+
new_lines.append(f"{parts[0]}\t" + " ".join(remaining_hosts))
|
|
151
|
+
removed = True
|
|
152
|
+
else:
|
|
153
|
+
new_lines.append(line)
|
|
154
|
+
|
|
155
|
+
if not removed:
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
new_content = "\r\n".join(new_lines) + "\r\n"
|
|
159
|
+
if is_admin():
|
|
160
|
+
_ensure_writable(paths.HOSTS_PATH)
|
|
161
|
+
paths.HOSTS_PATH.write_text(new_content, encoding="utf-8")
|
|
162
|
+
return True
|
|
163
|
+
|
|
164
|
+
b64_content = base64.b64encode(new_content.encode("utf-8")).decode("ascii")
|
|
165
|
+
ps_script = (
|
|
166
|
+
f"Set-ItemProperty -Path '{paths.HOSTS_PATH}' -Name IsReadOnly -Value $false -ErrorAction SilentlyContinue; "
|
|
167
|
+
f"$bytes = [System.Convert]::FromBase64String('{b64_content}'); "
|
|
168
|
+
f"[System.IO.File]::WriteAllBytes('{paths.HOSTS_PATH}', $bytes)"
|
|
169
|
+
)
|
|
170
|
+
encoded = base64.b64encode(ps_script.encode("utf-16le")).decode("ascii")
|
|
171
|
+
exit_code = run_elevated(["powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded])
|
|
172
|
+
if exit_code != 0:
|
|
173
|
+
raise RuntimeError(f"Failed to update hosts file (exit code {exit_code})")
|
|
174
|
+
return True
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def create_vhost(domain: str, root: str | Path, php_version: str, ssl: bool = False,
|
|
178
|
+
auto_start_pool: bool = True) -> Path:
|
|
179
|
+
"""
|
|
180
|
+
Creates an Nginx virtual host, ensures php-cgi pool is running, and maps to hosts file.
|
|
181
|
+
"""
|
|
182
|
+
root_path = Path(root).resolve()
|
|
183
|
+
if not root_path.exists():
|
|
184
|
+
root_path.mkdir(parents=True, exist_ok=True)
|
|
185
|
+
if not any(root_path.iterdir()):
|
|
186
|
+
(root_path / "index.php").write_text(
|
|
187
|
+
f"<?php\n"
|
|
188
|
+
f"// Virtual host: {domain}\n"
|
|
189
|
+
f"echo '<h1>Welcome to {domain}</h1>';\n"
|
|
190
|
+
f"echo '<p>Served by Nginx & PHP {php_version} via ndev</p>';\n"
|
|
191
|
+
f"phpinfo();\n",
|
|
192
|
+
encoding="utf-8"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Local import to avoid circular dependencies
|
|
196
|
+
from . import php, services
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
resolved_php = php.resolve_installed(php_version)
|
|
200
|
+
except Exception:
|
|
201
|
+
resolved_php = php_version
|
|
202
|
+
|
|
203
|
+
if not fcgi.status(resolved_php):
|
|
204
|
+
if not auto_start_pool:
|
|
205
|
+
raise RuntimeError(
|
|
206
|
+
f"PHP {resolved_php} pool is not running -- run "
|
|
207
|
+
f"`ndev pool start {resolved_php}` first, or pass auto_start_pool=True"
|
|
208
|
+
)
|
|
209
|
+
cfg = paths.load_config()
|
|
210
|
+
fcgi.start(resolved_php, php.php_cgi_exe(resolved_php),
|
|
211
|
+
cfg["fcgi_workers_per_version"], cfg["fcgi_base_port"])
|
|
212
|
+
|
|
213
|
+
conf_path = write_vhost_conf(domain, root_path, resolved_php, ssl=ssl)
|
|
214
|
+
add_hosts_entry(domain)
|
|
215
|
+
|
|
216
|
+
# If Nginx is installed and running, reload it
|
|
217
|
+
if (paths.NGINX_DIR / "nginx.exe").exists():
|
|
218
|
+
try:
|
|
219
|
+
services.nginx_reload()
|
|
220
|
+
except Exception:
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
return conf_path
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def remove_vhost(domain: str) -> bool:
|
|
227
|
+
"""Remove a virtual host config and hosts file entry."""
|
|
228
|
+
clean_domain = re.sub(r"^https?://", "", domain.strip().lower()).rstrip("/")
|
|
229
|
+
conf_path = paths.NGINX_CONF_D / f"{clean_domain}.conf"
|
|
230
|
+
cert_dir = paths.CERTS_DIR / clean_domain
|
|
231
|
+
if not conf_path.exists() and not cert_dir.exists():
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
removed = False
|
|
235
|
+
if conf_path.exists():
|
|
236
|
+
conf_path.unlink()
|
|
237
|
+
removed = True
|
|
238
|
+
remove_hosts_entry(clean_domain)
|
|
239
|
+
|
|
240
|
+
# Clean up SSL cert directory if present
|
|
241
|
+
if cert_dir.exists():
|
|
242
|
+
import shutil
|
|
243
|
+
try:
|
|
244
|
+
shutil.rmtree(cert_dir)
|
|
245
|
+
except Exception:
|
|
246
|
+
pass
|
|
247
|
+
|
|
248
|
+
# If Nginx is installed and running, reload it
|
|
249
|
+
from . import services
|
|
250
|
+
if (paths.NGINX_DIR / "nginx.exe").exists():
|
|
251
|
+
try:
|
|
252
|
+
services.nginx_reload()
|
|
253
|
+
except Exception:
|
|
254
|
+
pass
|
|
255
|
+
|
|
256
|
+
return removed
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def list_vhosts() -> list[dict]:
|
|
260
|
+
"""List all configured virtual hosts."""
|
|
261
|
+
if not paths.NGINX_CONF_D.exists():
|
|
262
|
+
return []
|
|
263
|
+
vhosts = []
|
|
264
|
+
for p in sorted(paths.NGINX_CONF_D.glob("*.conf")):
|
|
265
|
+
if p.name.startswith("_"):
|
|
266
|
+
continue
|
|
267
|
+
domain = p.stem
|
|
268
|
+
content = p.read_text(encoding="utf-8", errors="ignore")
|
|
269
|
+
root_m = re.search(r'root\s+["\']?([^";\r\n]+?)["\']?;', content)
|
|
270
|
+
root = root_m.group(1).strip() if root_m else "N/A"
|
|
271
|
+
|
|
272
|
+
# Check explicit comment header first, then upstream pattern
|
|
273
|
+
m_comment = re.search(r'#\s*ndev-php:\s*([\w.\-]+)', content)
|
|
274
|
+
if m_comment:
|
|
275
|
+
php_ver = m_comment.group(1)
|
|
276
|
+
else:
|
|
277
|
+
clean_domain = re.sub(r"[^a-zA-Z0-9_]", "_", domain.lower())
|
|
278
|
+
m_up = re.search(rf'upstream\s+php_([\d_]+)_{clean_domain}\b', content) or re.search(r'upstream\s+php_([\d_]+)\b', content)
|
|
279
|
+
php_ver = m_up.group(1).replace("_", ".") if m_up else "unknown"
|
|
280
|
+
|
|
281
|
+
has_ssl = "listen 443 ssl" in content
|
|
282
|
+
vhosts.append({
|
|
283
|
+
"domain": domain,
|
|
284
|
+
"root": root,
|
|
285
|
+
"php": php_ver,
|
|
286
|
+
"ssl": has_ssl,
|
|
287
|
+
"conf": str(p),
|
|
288
|
+
})
|
|
289
|
+
return vhosts
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{upstream_block}
|
|
2
|
+
server {{
|
|
3
|
+
listen 80;
|
|
4
|
+
server_name {domain};
|
|
5
|
+
root "{root}";
|
|
6
|
+
index index.php index.html index.htm;
|
|
7
|
+
client_max_body_size 128M;
|
|
8
|
+
|
|
9
|
+
access_log "{ndev_home}/nginx/logs/{domain}.access.log";
|
|
10
|
+
error_log "{ndev_home}/nginx/logs/{domain}.error.log";
|
|
11
|
+
|
|
12
|
+
location / {{
|
|
13
|
+
try_files $uri $uri/ /index.php?$query_string;
|
|
14
|
+
}}
|
|
15
|
+
|
|
16
|
+
location ~ \.php$ {{
|
|
17
|
+
fastcgi_pass {upstream_name};
|
|
18
|
+
fastcgi_index index.php;
|
|
19
|
+
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|
20
|
+
include fastcgi_params;
|
|
21
|
+
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
22
|
+
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|
23
|
+
fastcgi_buffer_size 32k;
|
|
24
|
+
fastcgi_buffers 16 16k;
|
|
25
|
+
fastcgi_busy_buffers_size 64k;
|
|
26
|
+
fastcgi_read_timeout 300;
|
|
27
|
+
fastcgi_send_timeout 300;
|
|
28
|
+
}}
|
|
29
|
+
|
|
30
|
+
location ~ /\.ht {{
|
|
31
|
+
deny all;
|
|
32
|
+
}}
|
|
33
|
+
}}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
{upstream_block}
|
|
3
|
+
server {{
|
|
4
|
+
listen 80;
|
|
5
|
+
server_name {domain};
|
|
6
|
+
return 301 https://$host$request_uri;
|
|
7
|
+
}}
|
|
8
|
+
|
|
9
|
+
server {{
|
|
10
|
+
listen 443 ssl;
|
|
11
|
+
server_name {domain};
|
|
12
|
+
root "{root}";
|
|
13
|
+
index index.php index.html index.htm;
|
|
14
|
+
client_max_body_size 128M;
|
|
15
|
+
|
|
16
|
+
ssl_certificate "{cert_path}";
|
|
17
|
+
ssl_certificate_key "{key_path}";
|
|
18
|
+
|
|
19
|
+
access_log "{ndev_home}/nginx/logs/{domain}.ssl.access.log";
|
|
20
|
+
error_log "{ndev_home}/nginx/logs/{domain}.ssl.error.log";
|
|
21
|
+
|
|
22
|
+
location / {{
|
|
23
|
+
try_files $uri $uri/ /index.php?$query_string;
|
|
24
|
+
}}
|
|
25
|
+
|
|
26
|
+
location ~ \.php$ {{
|
|
27
|
+
fastcgi_pass {upstream_name};
|
|
28
|
+
fastcgi_index index.php;
|
|
29
|
+
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|
30
|
+
include fastcgi_params;
|
|
31
|
+
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
32
|
+
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|
33
|
+
fastcgi_buffer_size 32k;
|
|
34
|
+
fastcgi_buffers 16 16k;
|
|
35
|
+
fastcgi_busy_buffers_size 64k;
|
|
36
|
+
fastcgi_read_timeout 300;
|
|
37
|
+
fastcgi_send_timeout 300;
|
|
38
|
+
}}
|
|
39
|
+
|
|
40
|
+
location ~ /\.ht {{
|
|
41
|
+
deny all;
|
|
42
|
+
}}
|
|
43
|
+
}}
|