From d28e171693c16add8c0a0929b450f84fb3702ccd Mon Sep 17 00:00:00 2001 From: shenlan Date: Wed, 6 Aug 2025 09:27:24 +0800 Subject: [PATCH] feat(openresty): template sites configs --- .../roles/vhosts/OpenResty/tasks/main.yml | 12 ++ .../vhosts/OpenResty/templates/nginx.conf.j2 | 86 ++++-------- .../vhosts/OpenResty/templates/site.conf.j2 | 128 ++++++++++++++++++ 3 files changed, 167 insertions(+), 59 deletions(-) create mode 100644 playbooks/roles/vhosts/OpenResty/templates/site.conf.j2 diff --git a/playbooks/roles/vhosts/OpenResty/tasks/main.yml b/playbooks/roles/vhosts/OpenResty/tasks/main.yml index fe46386..f01c3f2 100644 --- a/playbooks/roles/vhosts/OpenResty/tasks/main.yml +++ b/playbooks/roles/vhosts/OpenResty/tasks/main.yml @@ -25,12 +25,24 @@ state: present update_cache: yes +- name: Ensure sites-available directory exists + file: + path: /usr/local/openresty/nginx/conf/sites-available + state: directory + - name: Deploy nginx configuration template: src: nginx.conf.j2 dest: /usr/local/openresty/nginx/conf/nginx.conf notify: Restart OpenResty +- name: Deploy vhost configurations + template: + src: site.conf.j2 + dest: "/usr/local/openresty/nginx/conf/sites-available/{{ item.name }}.conf" + loop: "{{ vhosts_openresty_vhosts | default([]) }}" + notify: Restart OpenResty + - name: Enable and start OpenResty systemd: name: openresty diff --git a/playbooks/roles/vhosts/OpenResty/templates/nginx.conf.j2 b/playbooks/roles/vhosts/OpenResty/templates/nginx.conf.j2 index ebeaa4a..bce5dcc 100644 --- a/playbooks/roles/vhosts/OpenResty/templates/nginx.conf.j2 +++ b/playbooks/roles/vhosts/OpenResty/templates/nginx.conf.j2 @@ -1,62 +1,30 @@ -worker_processes auto; -events { worker_connections 1024; } +worker_processes auto; -http { - lua_shared_dict limit_cache 10m; - -{% for site in domain %} - server { - listen 80; - listen 443 ssl; - server_name {{ site.name }}; - ssl_certificate {{ site.ssl_certificate }}; - ssl_certificate_key {{ site.ssl_certificate_key }}; - - location /api/askai { - access_by_lua_block { - local redis = require "resty.redis" - local r = redis:new() - r:set_timeout(200) - - -- 连接 Redis - local ok, err = r:connect("127.0.0.1", 6379) - if not ok then - ngx.log(ngx.ERR, "failed to connect to redis: ", err) - return ngx.exit(500) - end - - -- 用户标识(优先 token 参数,否则用 IP) - local user = ngx.var.arg_user or ngx.var.remote_addr - local today = os.date("%Y%m%d") - local user_key = "limit:user:" .. user .. ":" .. today - local global_key = "limit:global:" .. today - - -- 用户限额 200 - local current, err = r:incr(user_key) - if current == 1 then - r:expire(user_key, 86400) - end - if current > 200 then - ngx.status = 429 - ngx.say("Too Many Requests: user limit reached") - return ngx.exit(429) - end - - -- 全局限额 20000 - local gcount, err = r:incr(global_key) - if gcount == 1 then - r:expire(global_key, 86400) - end - if gcount > 20000 then - ngx.status = 429 - ngx.say("Too Many Requests: global limit reached") - return ngx.exit(429) - end - } - - proxy_pass http://127.0.0.1:5000; - } - } -{% endfor %} +events { + worker_connections 1024; } +http { + include mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + # 开启 Gzip (可选) + gzip on; + gzip_types text/plain text/css application/json application/javascript application/xml+rss; + + # 定义日志格式(可选) + log_format main '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '$request_time'; + + # 全局访问日志 + access_log /usr/local/openresty/nginx/logs/access.log main; + error_log /usr/local/openresty/nginx/logs/error.log warn; + + # 引入 sites-available 下的配置 + include /usr/local/openresty/nginx/conf/sites-available/*.conf; +} diff --git a/playbooks/roles/vhosts/OpenResty/templates/site.conf.j2 b/playbooks/roles/vhosts/OpenResty/templates/site.conf.j2 new file mode 100644 index 0000000..fada5ab --- /dev/null +++ b/playbooks/roles/vhosts/OpenResty/templates/site.conf.j2 @@ -0,0 +1,128 @@ +lua_package_path "/usr/local/openresty/lualib/?.lua;;"; + +{% for sub in item.subdomains %} +{% if sub.type == 'artifact' %} +# {{ sub.server_name }} 文件下载服务 +server { + listen 443 ssl http2; + server_name {{ sub.server_name }}; + + ssl_certificate {{ sub.ssl_certificate }}; + ssl_certificate_key {{ sub.ssl_certificate_key }}; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + root {{ sub.root }}; + index index.html; + + autoindex on; + autoindex_exact_size off; + autoindex_localtime on; + + location / { + add_header Accept-Ranges bytes; + try_files $uri $uri/ =404; + } + + location ~* \.(dmg|zip|tar\.gz|deb|rpm|exe|pkg|AppImage|apk|ipa)$ { + expires 7d; + access_log off; + add_header Cache-Control "public"; + add_header Accept-Ranges bytes; + try_files $uri =404; + } + + location ~ /\. { + deny all; + } +} +{% else %} +# HTTP → HTTPS +server { + listen 80; + server_name {{ sub.server_name }}; + return 301 https://{{ sub.server_name }}$request_uri; +} + +# 主站服务 +server { + listen 443 ssl http2; + server_name {{ sub.server_name }}; + + ssl_certificate {{ sub.ssl_certificate }}; + ssl_certificate_key {{ sub.ssl_certificate_key }}; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + root {{ sub.root }}; + index index.html; + + {% if sub.askai_backend is defined %} + # /api/askai 限流:每用户每日 200 次 + location = /api/askai { + access_by_lua_block { + local redis = require "resty.redis" + local r = redis:new() + r:set_timeout(200) + + local ok, err = r:connect("127.0.0.1", 6379) + if not ok then + ngx.log(ngx.ERR, "failed to connect to redis: ", err) + return ngx.exit(500) + end + + local user = ngx.var.arg_user or ngx.var.remote_addr + local today = os.date("%Y%m%d") + local user_key = "limit:user:" .. user .. ":" .. today + + local current, err = r:incr(user_key) + if current == 1 then + r:expire(user_key, 86400) + end + if current > 200 then + ngx.status = 429 + ngx.say("Too Many Requests: daily limit reached") + return ngx.exit(429) + end + } + + proxy_pass {{ sub.askai_backend }}; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + {% endif %} + + {% if sub.api_backend is defined %} + # 其他 API + location /api/ { + proxy_pass {{ sub.api_backend }}; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + {% endif %} + + # SPA fallback + location / { + try_files $uri $uri/ /index.html; + } + + # 静态资源缓存 + location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?)$ { + expires 30d; + access_log off; + add_header Cache-Control "public"; + } + + # 隐藏 . 文件 + location ~ /\. { + deny all; + } +} +{% endif %} +{% endfor %}