diff --git a/.gitignore b/.gitignore index 700d0dd..ab2d2f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /log /db /ui +/ssl **/*.js tc.json diff --git a/cli/start.zsh b/cli/start.zsh index 22fb62b..ba6e3ac 100644 --- a/cli/start.zsh +++ b/cli/start.zsh @@ -4,5 +4,6 @@ zsh cli/db.zsh curl "http://localhost:728/quit" sleep 0.5 - deno run --allow-net --allow-read cli/ser.js + +nginx -p . -c nginx.conf diff --git a/cli/stop.zsh b/cli/stop.zsh index 96e446d..6db06b0 100644 --- a/cli/stop.zsh +++ b/cli/stop.zsh @@ -1,2 +1,3 @@ +nginx -squit mongosh --eval "db.shutdownServer()" curl "http://localhost:728/quit" diff --git a/ismism.ts/ui/bind/fetch.ts b/ismism.ts/ui/bind/fetch.ts index 3923ba0..4394d54 100644 --- a/ismism.ts/ui/bind/fetch.ts +++ b/ismism.ts/ui/bind/fetch.ts @@ -1,9 +1,6 @@ import type { Que, QueRet } from "../../src/pra/que.ts" import { json_s } from "../../src/ont/json.ts" -// 服务:端口 -const ser = `http://localhost:728` - // 绑定数据(浏览器端)<->(服务器端) // 查询接口 @@ -13,7 +10,7 @@ export async function que< q: Que ) { const s = json_s(q) - const r = (await fetch(`${ser}/q?${s}`)).json() // HTTP GET 请求 + const r = (await fetch(`/q?${s}`)).json() // HTTP GET 请求 console.log(`GET ${s}`) return r as unknown as T } diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..914638d --- /dev/null +++ b/nginx.conf @@ -0,0 +1,191 @@ +worker_processes auto; # nginx 进程数与 CPU 核数一致 +worker_rlimit_nofile 10000; # nginx 可同时打开的文件数 + +events { # 事件处理配置 + worker_connections 2048; # 最大连接数(TCP)- 服务器最多支持 2048 个用户同时在线 + multi_accept on; # 支持 worker 进程一口气开启多个连接 +} + +http { + access_log log/access.log combined buffer=128k flush=5m; # 日志文件 + error_log log/error.log error; # 错误日志 + + client_header_timeout 10s; # 如果 10s 都还没有把 header 传完,拒绝请求 + client_body_timeout 10s; # 如果 10s 都还没有把 body 传完,拒绝请求 + send_timeout 10s; # 如果 10s 都还没有接受完 response,拒绝请求 + reset_timedout_connection on; # 如果超时,直接关掉 connection(释放所有占用的资源) + + gzip on; # 返回数据前,先用 gzip 压缩 + gzip_comp_level 4; + gzip_min_length 1000; # 如果数据长度小于 1000(字节),不压缩 + gzip_proxied any; # 动态数据也要压缩 + gzip_types *; # 压缩所有类型的数据 + + server_tokens off; + charset utf-8; + + map $server_protocol $h1_addr { + default ""; # 默认不标记 - HTTP/2 请求(https),不标记 + "HTTP/1.0" $binary_remote_addr; # 如果是 HTTP/1 的请求,标记其 ip 为 h1_addr + "HTTP/1.1" $binary_remote_addr; + } + + limit_conn_status 444; # 如果【连接数超标】,关闭连接 444 + limit_conn_zone $binary_remote_addr zone=addr_conn:10m; # ip 对应的十分钟内的连接数 + limit_conn_zone $h1_addr zone=h1_conn:10m; # 被标记为 h1_addr 的 ip 对应的十分钟内的连接数 + limit_req_status 444; # 如果【请求数超标】,关闭连接 444 + limit_req_zone $binary_remote_addr zone=addr_req:10m rate=100r/m; # ... 请求数,rate < 100r/m + limit_req_zone $h1_addr zone=h1_req:10m rate=10r/m; # 被标记为 h1_addr ... 请求数 rate < 10r/m + + limit_conn addr_conn 10; # 单个 ip 最多 10 个连接 + limit_req zone=addr_req burst=20 nodelay; # 最多同时一次性处理来自 ip 的 20 个请求 + limit_conn h1_conn 10; # 单个被标记为 h1_addr 的 ip 最多 10 个连接 + limit_req zone=h1_req burst=10 nodelay; # 最多同时一次性处理来自 ip 的 10 个请求 + + # 444 = 已读不回 + error_page 400 403 404 500 502 503 504 =444 /444.html; # 没有默认的错误页面,直接 444 关闭连接(例如,没有 404 页面) + + types { # 服务支持的数据类型 + text/html html; + text/css css; + application/javascript js; + application/json json; + application/pdf pdf; + image/webp webp; + } + + upstream ismism { # 服务的内部地址和端口 + server 127.0.0.1:728 fail_timeout=1h; # 默认端口,无响应的话转备用端口,1h 后再重试 + server 127.0.0.1:729 backup; + keepalive 2; # 哪怕没有请求,都至少常备 2 个连接,保证新请求总有现成连接可用 + } + + # 数据缓存的路径 - 放在 cache 目录 + proxy_cache_path cache levels=1:2 keys_zone=cache:10m max_size=10g inactive=1d use_temp_path=off; + + server { + server_name _; # "_" 指向任意域名的服务请求 例如:http://www.ismist.cn + listen 80 default_server; # HTTP/1 的默认端口(http://) + listen [::]:80 default_server; + listen 443 ssl default_server; # HTTP/2 的默认端口(https://) + listen [::]:443 ssl default_server; + + http2 on; # 打开 HTTP/2 协议 + ssl_protocols TLSv1.3; # 选用 1.3 加密算法 + ssl_certificate ssl/ismist.cn.crt; # 域名证书 + ssl_certificate_key ssl/ismist.cn.key; # 域名签名 + ssl_session_cache shared:SSL:50m; + ssl_session_timeout 1d; + ssl_session_tickets off; + ssl_stapling on; + ssl_stapling_verify on; + + location = /444.html { + return 444; # return 444 = 已读不回(关掉连接) ismist.cn/wiejfiejf + } + + return 444; # 默认 已读不回 + } + + server { # 指向 ismist.cn 的 http 请求 例如 http://ismist.cn + server_name ismist.cn; + listen 80; + listen [::]:80; + + location = /444.html { + return 444; + } + + location / { + return 444; # 已读不回 + } + + location = / { # "ismist.cn" + limit_except GET { # 只接受 GET 请求 + deny all; # 不然就 444 + } + return 301 https://$host; # 告诉浏览器 http://ismist.cn 应跳转到 https://ismist.cn + } + } + server { # 指向 ismist.cn 的 https 请求 例如 https://ismist.cn https://ismist.cn/q?adm + server_name ismist.cn localhost; # 额外加入 localhost + listen 443 ssl; + listen [::]:443 ssl; + + http2 on; # 打开 HTTP/2 协议 + ssl_protocols TLSv1.3; # 选用 1.3 加密算法 + ssl_certificate ssl/ismist.cn.crt; # 域名证书 + ssl_certificate_key ssl/ismist.cn.key; # 域名签名 + ssl_session_cache shared:SSL:50m; + ssl_session_timeout 1d; + ssl_session_tickets off; + ssl_stapling on; + ssl_stapling_verify on; + + root ui; # 静态文件的位置 + + location = /444.html { + return 444; + } + + # nginx 默认是 前缀匹配 + # 请求指向前缀匹配度最高的 location (= 完全匹配度最高) + + location / { # 如果请求不在下列声明,则已读不回 444 (/ 任意路径) + return 444; + } + + location = / { # 空路径 = "ismist.cn" + limit_except GET { + deny all; + } + try_files /index.html =444; # 试图返回 root 目录下的 /index.html 文件 + } + + location /mod { # 前缀为 /mod 的请求 + limit_except GET { # 只接受 GET 请求 + deny all; # 不然就 444 + } + } + + location /wsl { # 前缀为 /wsl 的请求 + limit_except GET { + deny all; + } + root ui/mod; + # 把 hostname(如域名)替换成 root 就是请求的静态文件 + # ismist.cn/wsl -> ui/mod/wsl/index.html (即默认返回 index) + # ismist.cn/wsl/ddd.pdf -> ui/mod/wsl/ddd.pdf + index index.html; # 如果路径没有进一步指向文件,默认返回 index.html。不然就找对应的文件 + } + + # proxy 代理:把请求转发给指定的内部服务 + + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + proxy_cache cache; + proxy_cache_methods GET; # 只对 GET 请求的数据做缓存 + proxy_cache_valid any 1s; # any 任意状态 包括 200 304 + proxy_cache_revalidate on; # 如果超过 1s 则确认缓存版本 + proxy_cache_background_update on; + proxy_cache_lock on; + proxy_http_version 1.1; # 内部服务用 http 而非 https 内部服务不加密 + proxy_set_header Connection ""; # 内部服务可以不关闭连接。复用内部连接 + + location /q { # 如 HTTP GET ismist.cn/q?adm + limit_except GET { + deny all; + } + # https://ismist.cn/q?adm <=> http://127.0.0.1:728/q?adm + proxy_pass http://ismism; + } + + location /p { # 如 HTTP POST ismist.cn/p + JSON + limit_except POST { + deny all; + } + proxy_pass http://ismism; + } + } +} diff --git a/readme.md b/readme.md index 32ae345..b12fd67 100644 --- a/readme.md +++ b/readme.md @@ -58,6 +58,7 @@ - `start.zsh` 启动服务 * `*ui` 图形界面(运行环境)(浏览器端) - `*index.html` 网页界面 +* `*ssl` 域名证书和签名 * `mongod.service` `mongod.yaml` 数据库配置(服务器端) * `nginx.conf` 端口配置(服务器端)