Template rag server configuration
This commit is contained in:
parent
7463891002
commit
90b819e5ad
113
playbooks/roles/docker/XControl/README.md
Normal file
113
playbooks/roles/docker/XControl/README.md
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# XControl Docker role
|
||||||
|
|
||||||
|
This role provisions the XControl stack (Postgres, account service, RAG server, dashboard, Redis, and Nginx proxy with Certbot assets). Templates from `templates/` and static assets from `files/` are rendered into `{{ xcontrol_workspace }}` and the Docker Compose stack is started.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
```
|
||||||
|
files/
|
||||||
|
├── docker-compose.yaml
|
||||||
|
├── config/
|
||||||
|
│ ├── account.yaml
|
||||||
|
│ └── server.yaml
|
||||||
|
├── certbot/
|
||||||
|
│ ├── conf/
|
||||||
|
│ └── www/
|
||||||
|
├── nginx/
|
||||||
|
│ ├── conf.d/
|
||||||
|
│ │ ├── accounts.conf
|
||||||
|
│ │ ├── artifact.conf
|
||||||
|
│ │ ├── bootstrap-nginx.conf
|
||||||
|
│ │ ├── default.conf
|
||||||
|
│ │ ├── homepage.conf
|
||||||
|
│ │ └── rag-server.conf
|
||||||
|
│ └── nginx.conf
|
||||||
|
└── run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Defaults
|
||||||
|
- `xcontrol_deploy_dir`: `/opt/xcontrol`
|
||||||
|
- `xcontrol_workspace`: `{{ xcontrol_deploy_dir }}`
|
||||||
|
- `xcontrol_certbot_domains`: `svc.plus` (comma-separated)
|
||||||
|
- `xcontrol_certbot_email`: `manbuzhe2009@qq.com`
|
||||||
|
- `xcontrol_homepage_domain`: `{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_homepage_alias_domain`: `www.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_homepage_cn_domain`: `cn-homepage.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_accounts_domain`: `accounts.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_rag_domain`: `rag-server.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_rag_api_domain`: `api.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_artifact_domain`: `dl.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_artifact_cn_domain`: `cn-dl.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_db_host`: `db`
|
||||||
|
- `xcontrol_db_port`: `5432`
|
||||||
|
- `xcontrol_db_name`: `xcontrol`
|
||||||
|
- `xcontrol_db_user`: `xcontrol`
|
||||||
|
- `xcontrol_db_password`: `xcontrol`
|
||||||
|
- `xcontrol_account_mode`: `server-agent`
|
||||||
|
- `xcontrol_account_log_level`: `info`
|
||||||
|
- `xcontrol_account_auth_enable`: `true`
|
||||||
|
- `xcontrol_account_public_token`: `xcontrol-public-token-2024`
|
||||||
|
- `xcontrol_account_refresh_secret`: `xcontrol-refresh-secret-2024`
|
||||||
|
- `xcontrol_account_access_secret`: `xcontrol-access-secret-2024`
|
||||||
|
- `xcontrol_account_access_expiry`: `1h`
|
||||||
|
- `xcontrol_account_refresh_expiry`: `168h`
|
||||||
|
- `xcontrol_account_server_addr`: `:8080`
|
||||||
|
- `xcontrol_account_read_timeout`: `15s`
|
||||||
|
- `xcontrol_account_write_timeout`: `15s`
|
||||||
|
- `xcontrol_account_public_url`: `https://accounts.{{ xcontrol_primary_domain }}`
|
||||||
|
- `xcontrol_account_tls_enabled`: `false`
|
||||||
|
- `xcontrol_account_tls_redirect_http`: `false`
|
||||||
|
- `xcontrol_account_store_driver`: `postgres`
|
||||||
|
- `xcontrol_account_db_name`: `{{ xcontrol_db_name }}`
|
||||||
|
- `xcontrol_account_db_sslmode`: `disable`
|
||||||
|
- `xcontrol_account_db_max_open_conns`: `30`
|
||||||
|
- `xcontrol_account_db_max_idle_conns`: `10`
|
||||||
|
- `xcontrol_account_session_ttl`: `24h`
|
||||||
|
- `xcontrol_account_session_cache`: `memory`
|
||||||
|
- `xcontrol_account_smtp_host`: `smtp.example.com`
|
||||||
|
- `xcontrol_account_smtp_port`: `587`
|
||||||
|
- `xcontrol_account_smtp_username`: `apikey`
|
||||||
|
- `xcontrol_account_smtp_password`: `change-me`
|
||||||
|
- `xcontrol_account_smtp_from`: `XControl Account <no-reply@example.com>`
|
||||||
|
- `xcontrol_account_smtp_timeout`: `10s`
|
||||||
|
- `xcontrol_account_smtp_tls_mode`: `auto`
|
||||||
|
- `xcontrol_account_smtp_tls_insecure_skip_verify`: `false`
|
||||||
|
- `xcontrol_account_xray_sync_enabled`: `false`
|
||||||
|
- `xcontrol_account_xray_sync_interval`: `5m`
|
||||||
|
- `xcontrol_account_xray_output_path`: `/usr/local/etc/xray/config.json`
|
||||||
|
- `xcontrol_account_xray_template_path`: `account/config/xray.config.template.json`
|
||||||
|
- `xcontrol_account_xray_validate_command`: `[]`
|
||||||
|
- `xcontrol_account_xray_restart_command`: `["systemctl", "restart", "xray.service"]`
|
||||||
|
- `xcontrol_account_agent_id`: `account-primary`
|
||||||
|
- `xcontrol_rag_server_addr`: `:8090`
|
||||||
|
- `xcontrol_rag_read_timeout`: `120s`
|
||||||
|
- `xcontrol_rag_write_timeout`: `120s`
|
||||||
|
- `xcontrol_rag_public_url`: `https://{{ xcontrol_rag_api_domain }}`
|
||||||
|
- `xcontrol_rag_allowed_origins`: `["https://{{ xcontrol_rag_api_domain }}", "https://{{ xcontrol_homepage_alias_domain }}", "https://{{ xcontrol_homepage_domain }}", "https://{{ xcontrol_accounts_domain }}", "http://localhost:3000", "http://127.0.0.1:3000"]`
|
||||||
|
- `xcontrol_rag_auth_enable`: `false`
|
||||||
|
- `xcontrol_rag_auth_url`: `https://{{ xcontrol_accounts_domain }}`
|
||||||
|
- `xcontrol_rag_api_base_url`: `https://{{ xcontrol_rag_api_domain }}`
|
||||||
|
- `xcontrol_rag_public_token`: `xcontrol-public-token-2025`
|
||||||
|
- `xcontrol_rag_redis_addr`: `""`
|
||||||
|
- `xcontrol_rag_redis_password`: `""`
|
||||||
|
- `xcontrol_rag_vectordb_db_name`: `rag`
|
||||||
|
- `xcontrol_rag_vectordb_sslmode`: `disable`
|
||||||
|
- `xcontrol_rag_vectordb_pgurl`: `postgres://{{ xcontrol_db_user }}:{{ xcontrol_db_password }}@{{ xcontrol_db_host }}:{{ xcontrol_db_port }}/{{ xcontrol_rag_vectordb_db_name }}?sslmode={{ xcontrol_rag_vectordb_sslmode }}`
|
||||||
|
- `xcontrol_rag_datasources`: `[{"name": "XControl", "repo": "https://github.com/svc-design/XControl", "path": "docs"}]`
|
||||||
|
- `xcontrol_rag_sync_repo_proxy`: `""`
|
||||||
|
- `xcontrol_rag_embedder_provider`: `chutes`
|
||||||
|
- `xcontrol_rag_embedder_models`: `["bge-m3"]`
|
||||||
|
- `xcontrol_rag_embedder_baseurl`: `http://127.0.0.1:9000`
|
||||||
|
- `xcontrol_rag_embedder_endpoint`: `http://127.0.0.1:9000/v1/embeddings`
|
||||||
|
- `xcontrol_rag_generator_provider`: `chutes`
|
||||||
|
- `xcontrol_rag_generator_models`: `["deepseek-r1:8b"]`
|
||||||
|
- `xcontrol_rag_generator_baseurl`: `http://127.0.0.1:11434`
|
||||||
|
- `xcontrol_rag_generator_endpoint`: `http://127.0.0.1:11434/v1/chat/completions`
|
||||||
|
- `xcontrol_rag_embedding_max_batch`: `64`
|
||||||
|
- `xcontrol_rag_embedding_dimension`: `1024`
|
||||||
|
|
||||||
|
## RUN
|
||||||
|
|
||||||
|
```
|
||||||
|
ansible-playbook -i inventory.ini deploy_XControl_docker.yaml -e "domain=svc.plus" -D -C -l svc.plus
|
||||||
|
ansible-playbook -i inventory.ini deploy_XControl_docker.yaml -e "domain=svc.plus" -D -l svc.plus
|
||||||
|
```
|
||||||
112
playbooks/roles/docker/XControl/defaults/main.yml
Normal file
112
playbooks/roles/docker/XControl/defaults/main.yml
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
# Default deployment directory for XControl Docker stack
|
||||||
|
xcontrol_deploy_dir: /opt/xcontrol
|
||||||
|
xcontrol_workspace: "{{ xcontrol_deploy_dir }}"
|
||||||
|
|
||||||
|
# Primary domain (first in the comma-separated domain list)
|
||||||
|
xcontrol_certbot_domains: svc.plus
|
||||||
|
xcontrol_primary_domain: "{{ xcontrol_certbot_domains.split(',')[0] | trim }}"
|
||||||
|
xcontrol_certbot_email: manbuzhe2009@qq.com
|
||||||
|
|
||||||
|
# Subdomains for individual services
|
||||||
|
xcontrol_homepage_domain: "{{ xcontrol_primary_domain }}"
|
||||||
|
xcontrol_homepage_alias_domain: "www.{{ xcontrol_primary_domain }}"
|
||||||
|
xcontrol_homepage_cn_domain: "cn-homepage.{{ xcontrol_primary_domain }}"
|
||||||
|
xcontrol_accounts_domain: "accounts.{{ xcontrol_primary_domain }}"
|
||||||
|
xcontrol_rag_domain: "rag-server.{{ xcontrol_primary_domain }}"
|
||||||
|
xcontrol_rag_api_domain: "api.{{ xcontrol_primary_domain }}"
|
||||||
|
xcontrol_artifact_domain: "dl.{{ xcontrol_primary_domain }}"
|
||||||
|
xcontrol_artifact_cn_domain: "cn-dl.{{ xcontrol_primary_domain }}"
|
||||||
|
|
||||||
|
# Database defaults
|
||||||
|
xcontrol_db_host: db
|
||||||
|
xcontrol_db_port: 5432
|
||||||
|
xcontrol_db_name: xcontrol
|
||||||
|
xcontrol_db_user: xcontrol
|
||||||
|
xcontrol_db_password: xcontrol
|
||||||
|
|
||||||
|
# Account service configuration defaults
|
||||||
|
xcontrol_account_mode: server-agent
|
||||||
|
xcontrol_account_log_level: info
|
||||||
|
xcontrol_account_auth_enable: true
|
||||||
|
xcontrol_account_public_token: xcontrol-public-token-2024
|
||||||
|
xcontrol_account_refresh_secret: xcontrol-refresh-secret-2024
|
||||||
|
xcontrol_account_access_secret: xcontrol-access-secret-2024
|
||||||
|
xcontrol_account_access_expiry: 1h
|
||||||
|
xcontrol_account_refresh_expiry: 168h
|
||||||
|
xcontrol_account_server_addr: ":8080"
|
||||||
|
xcontrol_account_read_timeout: 15s
|
||||||
|
xcontrol_account_write_timeout: 15s
|
||||||
|
xcontrol_account_public_url: "https://{{ xcontrol_accounts_domain }}"
|
||||||
|
xcontrol_account_tls_enabled: false
|
||||||
|
xcontrol_account_tls_redirect_http: false
|
||||||
|
xcontrol_account_store_driver: postgres
|
||||||
|
xcontrol_account_db_name: "{{ xcontrol_db_name }}"
|
||||||
|
xcontrol_account_db_sslmode: disable
|
||||||
|
xcontrol_account_db_max_open_conns: 30
|
||||||
|
xcontrol_account_db_max_idle_conns: 10
|
||||||
|
xcontrol_account_session_ttl: 24h
|
||||||
|
xcontrol_account_session_cache: memory
|
||||||
|
xcontrol_account_smtp_host: smtp.example.com
|
||||||
|
xcontrol_account_smtp_port: 587
|
||||||
|
xcontrol_account_smtp_username: apikey
|
||||||
|
xcontrol_account_smtp_password: change-me
|
||||||
|
xcontrol_account_smtp_from: "XControl Account <no-reply@example.com>"
|
||||||
|
xcontrol_account_smtp_timeout: 10s
|
||||||
|
xcontrol_account_smtp_tls_mode: auto
|
||||||
|
xcontrol_account_smtp_tls_insecure_skip_verify: false
|
||||||
|
xcontrol_account_xray_sync_enabled: false
|
||||||
|
xcontrol_account_xray_sync_interval: 5m
|
||||||
|
xcontrol_account_xray_output_path: /usr/local/etc/xray/config.json
|
||||||
|
xcontrol_account_xray_template_path: account/config/xray.config.template.json
|
||||||
|
xcontrol_account_xray_validate_command: []
|
||||||
|
xcontrol_account_xray_restart_command:
|
||||||
|
- systemctl
|
||||||
|
- restart
|
||||||
|
- xray.service
|
||||||
|
xcontrol_account_agent_id: account-primary
|
||||||
|
|
||||||
|
# Image overrides (optional)
|
||||||
|
xcontrol_account_image: ghcr.io/cloud-neutral-toolkit/account:latest
|
||||||
|
xcontrol_rag_image: manbuzhe2009/rag-server:latest
|
||||||
|
xcontrol_dashboard_image: manbuzhe2009/dashboard:latest
|
||||||
|
xcontrol_db_image: manbuzhe2009/postgres-runtime:latest
|
||||||
|
|
||||||
|
# RAG server configuration defaults
|
||||||
|
xcontrol_rag_server_addr: ":8090"
|
||||||
|
xcontrol_rag_read_timeout: 120s
|
||||||
|
xcontrol_rag_write_timeout: 120s
|
||||||
|
xcontrol_rag_public_url: "https://{{ xcontrol_rag_api_domain }}"
|
||||||
|
xcontrol_rag_allowed_origins:
|
||||||
|
- "https://{{ xcontrol_rag_api_domain }}"
|
||||||
|
- "https://{{ xcontrol_homepage_alias_domain }}"
|
||||||
|
- "https://{{ xcontrol_homepage_domain }}"
|
||||||
|
- "https://{{ xcontrol_accounts_domain }}"
|
||||||
|
- "http://localhost:3000"
|
||||||
|
- "http://127.0.0.1:3000"
|
||||||
|
xcontrol_rag_auth_enable: false
|
||||||
|
xcontrol_rag_auth_url: "https://{{ xcontrol_accounts_domain }}"
|
||||||
|
xcontrol_rag_api_base_url: "https://{{ xcontrol_rag_api_domain }}"
|
||||||
|
xcontrol_rag_public_token: xcontrol-public-token-2025
|
||||||
|
xcontrol_rag_redis_addr: ""
|
||||||
|
xcontrol_rag_redis_password: ""
|
||||||
|
xcontrol_rag_vectordb_db_name: rag
|
||||||
|
xcontrol_rag_vectordb_sslmode: disable
|
||||||
|
xcontrol_rag_vectordb_pgurl: "postgres://{{ xcontrol_db_user }}:{{ xcontrol_db_password }}@{{ xcontrol_db_host }}:{{ xcontrol_db_port }}/{{ xcontrol_rag_vectordb_db_name }}?sslmode={{ xcontrol_rag_vectordb_sslmode }}"
|
||||||
|
xcontrol_rag_datasources:
|
||||||
|
- name: XControl
|
||||||
|
repo: https://github.com/svc-design/XControl
|
||||||
|
path: docs
|
||||||
|
xcontrol_rag_sync_repo_proxy: ""
|
||||||
|
xcontrol_rag_embedder_provider: chutes
|
||||||
|
xcontrol_rag_embedder_models:
|
||||||
|
- bge-m3
|
||||||
|
xcontrol_rag_embedder_baseurl: http://127.0.0.1:9000
|
||||||
|
xcontrol_rag_embedder_endpoint: http://127.0.0.1:9000/v1/embeddings
|
||||||
|
xcontrol_rag_generator_provider: chutes
|
||||||
|
xcontrol_rag_generator_models:
|
||||||
|
- deepseek-r1:8b
|
||||||
|
xcontrol_rag_generator_baseurl: http://127.0.0.1:11434
|
||||||
|
xcontrol_rag_generator_endpoint: http://127.0.0.1:11434/v1/chat/completions
|
||||||
|
xcontrol_rag_embedding_max_batch: 64
|
||||||
|
xcontrol_rag_embedding_dimension: 1024
|
||||||
5
playbooks/roles/docker/XControl/files/nginx/nginx.conf
Normal file
5
playbooks/roles/docker/XControl/files/nginx/nginx.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
events {}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
||||||
6
playbooks/roles/docker/XControl/files/run.sh
Normal file
6
playbooks/roles/docker/XControl/files/run.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Helper script to start the XControl docker compose stack
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
docker compose -f docker-compose.yaml up -d
|
||||||
76
playbooks/roles/docker/XControl/tasks/main.yml
Normal file
76
playbooks/roles/docker/XControl/tasks/main.yml
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure XControl directories exist
|
||||||
|
become: true
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
loop:
|
||||||
|
- "{{ xcontrol_workspace }}"
|
||||||
|
- "{{ xcontrol_workspace }}/certbot"
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/conf"
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/www"
|
||||||
|
- "{{ xcontrol_workspace }}/config"
|
||||||
|
- "{{ xcontrol_workspace }}/nginx"
|
||||||
|
- "{{ xcontrol_workspace }}/nginx/conf.d"
|
||||||
|
|
||||||
|
- name: Ensure XControl workspace ownership
|
||||||
|
become: true
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ xcontrol_workspace }}"
|
||||||
|
state: directory
|
||||||
|
recurse: true
|
||||||
|
owner: "1000"
|
||||||
|
group: "1000"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Template XControl configuration files
|
||||||
|
become: true
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ xcontrol_workspace }}/{{ item.dest }}"
|
||||||
|
mode: "{{ item.mode | default('0644') }}"
|
||||||
|
loop:
|
||||||
|
- { src: 'docker-compose.yaml', dest: 'docker-compose.yaml' }
|
||||||
|
- { src: 'config/account.yaml', dest: 'config/account.yaml' }
|
||||||
|
- { src: 'config/server.yaml', dest: 'config/server.yaml' }
|
||||||
|
- { src: 'nginx/conf.d/default.conf', dest: 'nginx/conf.d/default.conf' }
|
||||||
|
- { src: 'nginx/conf.d/bootstrap-nginx.conf', dest: 'nginx/conf.d/bootstrap-nginx.conf' }
|
||||||
|
- { src: 'nginx/conf.d/accounts.conf', dest: 'nginx/conf.d/accounts.conf' }
|
||||||
|
- { src: 'nginx/conf.d/homepage.conf', dest: 'nginx/conf.d/homepage.conf' }
|
||||||
|
- { src: 'nginx/conf.d/rag-server.conf', dest: 'nginx/conf.d/rag-server.conf' }
|
||||||
|
- { src: 'nginx/conf.d/artifact.conf', dest: 'nginx/conf.d/artifact.conf' }
|
||||||
|
|
||||||
|
- name: Copy XControl static files
|
||||||
|
become: true
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ xcontrol_workspace }}/{{ item.dest }}"
|
||||||
|
mode: "{{ item.mode | default('0644') }}"
|
||||||
|
loop:
|
||||||
|
- { src: 'run.sh', dest: 'run.sh', mode: '0755' }
|
||||||
|
- { src: 'nginx/nginx.conf', dest: 'nginx/nginx.conf' }
|
||||||
|
|
||||||
|
- name: Bootstrap NGINX (80-only for ACME)
|
||||||
|
become: true
|
||||||
|
command: docker compose --profile bootstrap -f {{ xcontrol_workspace }}/docker-compose.yaml up -d bootstrap-nginx
|
||||||
|
args:
|
||||||
|
chdir: "{{ xcontrol_workspace }}"
|
||||||
|
|
||||||
|
- name: Run certbot initial ACME challenge
|
||||||
|
become: true
|
||||||
|
command: docker compose --profile bootstrap -f {{ xcontrol_workspace }}/docker-compose.yaml run --rm certbot
|
||||||
|
args:
|
||||||
|
chdir: "{{ xcontrol_workspace }}"
|
||||||
|
|
||||||
|
- name: Destroy Bootstrap NGINX (80-only for ACME)
|
||||||
|
become: true
|
||||||
|
command: docker compose --profile bootstrap -f {{ xcontrol_workspace }}/docker-compose.yaml down bootstrap-nginx
|
||||||
|
args:
|
||||||
|
chdir: "{{ xcontrol_workspace }}"
|
||||||
|
|
||||||
|
- name: Bring up XControl stack
|
||||||
|
become: true
|
||||||
|
command: docker compose -f {{ xcontrol_workspace }}/docker-compose.yaml up -d
|
||||||
|
args:
|
||||||
|
chdir: "{{ xcontrol_workspace }}"
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
mode: "{{ xcontrol_account_mode }}"
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: {{ xcontrol_account_log_level | to_nice_yaml(indent=0) | trim }}
|
||||||
|
|
||||||
|
auth:
|
||||||
|
enable: {{ xcontrol_account_auth_enable | bool | lower }}
|
||||||
|
token:
|
||||||
|
publicToken: "{{ xcontrol_account_public_token }}"
|
||||||
|
refreshSecret: "{{ xcontrol_account_refresh_secret }}"
|
||||||
|
accessSecret: "{{ xcontrol_account_access_secret }}"
|
||||||
|
accessExpiry: "{{ xcontrol_account_access_expiry }}"
|
||||||
|
refreshExpiry: "{{ xcontrol_account_refresh_expiry }}"
|
||||||
|
|
||||||
|
server:
|
||||||
|
addr: "{{ xcontrol_account_server_addr }}"
|
||||||
|
readTimeout: {{ xcontrol_account_read_timeout | to_nice_yaml(indent=0) | trim }}
|
||||||
|
writeTimeout: {{ xcontrol_account_write_timeout | to_nice_yaml(indent=0) | trim }}
|
||||||
|
publicUrl: "{{ xcontrol_account_public_url }}"
|
||||||
|
allowedOrigins:
|
||||||
|
- "https://{{ xcontrol_accounts_domain }}"
|
||||||
|
- "https://{{ xcontrol_rag_api_domain }}"
|
||||||
|
- "https://{{ xcontrol_homepage_alias_domain }}"
|
||||||
|
- "http://localhost:3000"
|
||||||
|
- "http://127.0.0.1:3000"
|
||||||
|
- "http://localhost:8080"
|
||||||
|
- "http://127.0.0.1:8080"
|
||||||
|
tls:
|
||||||
|
enabled: {{ xcontrol_account_tls_enabled | bool | lower }}
|
||||||
|
redirectHttp: {{ xcontrol_account_tls_redirect_http | bool | lower }}
|
||||||
|
|
||||||
|
store:
|
||||||
|
driver: "{{ xcontrol_account_store_driver }}"
|
||||||
|
dsn: "postgres://{{ xcontrol_db_user }}:{{ xcontrol_db_password }}@{{ xcontrol_db_host }}:{{ xcontrol_db_port }}/{{ xcontrol_account_db_name }}?sslmode={{ xcontrol_account_db_sslmode }}"
|
||||||
|
maxOpenConns: {{ xcontrol_account_db_max_open_conns }}
|
||||||
|
maxIdleConns: {{ xcontrol_account_db_max_idle_conns }}
|
||||||
|
|
||||||
|
session:
|
||||||
|
ttl: {{ xcontrol_account_session_ttl | to_nice_yaml(indent=0) | trim }}
|
||||||
|
cache: "{{ xcontrol_account_session_cache }}"
|
||||||
|
|
||||||
|
smtp:
|
||||||
|
host: "{{ xcontrol_account_smtp_host }}"
|
||||||
|
port: {{ xcontrol_account_smtp_port }}
|
||||||
|
username: "{{ xcontrol_account_smtp_username }}"
|
||||||
|
password: "{{ xcontrol_account_smtp_password }}"
|
||||||
|
from: "{{ xcontrol_account_smtp_from }}"
|
||||||
|
timeout: {{ xcontrol_account_smtp_timeout | to_nice_yaml(indent=0) | trim }}
|
||||||
|
tls:
|
||||||
|
mode: "{{ xcontrol_account_smtp_tls_mode }}"
|
||||||
|
insecureSkipVerify: {{ xcontrol_account_smtp_tls_insecure_skip_verify | bool | lower }}
|
||||||
|
|
||||||
|
xray:
|
||||||
|
sync:
|
||||||
|
enabled: {{ xcontrol_account_xray_sync_enabled | bool | lower }}
|
||||||
|
interval: {{ xcontrol_account_xray_sync_interval | to_nice_yaml(indent=0) | trim }}
|
||||||
|
outputPath: "{{ xcontrol_account_xray_output_path }}"
|
||||||
|
templatePath: "{{ xcontrol_account_xray_template_path }}"
|
||||||
|
validateCommand: {{ xcontrol_account_xray_validate_command | to_nice_yaml(indent=2) | trim }}
|
||||||
|
restartCommand: {{ xcontrol_account_xray_restart_command | to_nice_yaml(indent=2) | trim }}
|
||||||
|
|
||||||
|
agent:
|
||||||
|
id: "{{ xcontrol_account_agent_id }}"
|
||||||
54
playbooks/roles/docker/XControl/templates/config/server.yaml
Normal file
54
playbooks/roles/docker/XControl/templates/config/server.yaml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
server:
|
||||||
|
addr: "{{ xcontrol_rag_server_addr }}"
|
||||||
|
readTimeout: "{{ xcontrol_rag_read_timeout }}"
|
||||||
|
writeTimeout: "{{ xcontrol_rag_write_timeout }}"
|
||||||
|
publicUrl: "{{ xcontrol_rag_public_url }}"
|
||||||
|
allowedOrigins:
|
||||||
|
{% for origin in xcontrol_rag_allowed_origins %}
|
||||||
|
- "{{ origin }}"
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
auth:
|
||||||
|
enable: {{ xcontrol_rag_auth_enable | bool }}
|
||||||
|
authUrl: "{{ xcontrol_rag_auth_url }}"
|
||||||
|
apiBaseUrl: "{{ xcontrol_rag_api_base_url }}"
|
||||||
|
publicToken: "{{ xcontrol_rag_public_token }}"
|
||||||
|
|
||||||
|
global:
|
||||||
|
redis:
|
||||||
|
addr: "{{ xcontrol_rag_redis_addr }}"
|
||||||
|
password: "{{ xcontrol_rag_redis_password }}"
|
||||||
|
vectordb:
|
||||||
|
pgurl: "{{ xcontrol_rag_vectordb_pgurl }}"
|
||||||
|
datasources:
|
||||||
|
{% for datasource in xcontrol_rag_datasources %}
|
||||||
|
- name: "{{ datasource.name }}"
|
||||||
|
repo: "{{ datasource.repo }}"
|
||||||
|
path: "{{ datasource.path }}"
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
sync:
|
||||||
|
repo:
|
||||||
|
proxy: "{{ xcontrol_rag_sync_repo_proxy }}"
|
||||||
|
|
||||||
|
models:
|
||||||
|
embedder:
|
||||||
|
provider: "{{ xcontrol_rag_embedder_provider }}"
|
||||||
|
models:
|
||||||
|
{% for model in xcontrol_rag_embedder_models %}
|
||||||
|
- "{{ model }}"
|
||||||
|
{% endfor %}
|
||||||
|
baseurl: "{{ xcontrol_rag_embedder_baseurl }}"
|
||||||
|
endpoint: "{{ xcontrol_rag_embedder_endpoint }}"
|
||||||
|
generator:
|
||||||
|
provider: "{{ xcontrol_rag_generator_provider }}"
|
||||||
|
models:
|
||||||
|
{% for model in xcontrol_rag_generator_models %}
|
||||||
|
- "{{ model }}"
|
||||||
|
{% endfor %}
|
||||||
|
baseurl: "{{ xcontrol_rag_generator_baseurl }}"
|
||||||
|
endpoint: "{{ xcontrol_rag_generator_endpoint }}"
|
||||||
|
|
||||||
|
embedding:
|
||||||
|
max_batch: {{ xcontrol_rag_embedding_max_batch }}
|
||||||
|
dimension: {{ xcontrol_rag_embedding_dimension }}
|
||||||
147
playbooks/roles/docker/XControl/templates/docker-compose.yaml
Normal file
147
playbooks/roles/docker/XControl/templates/docker-compose.yaml
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: "{{ xcontrol_db_image }}"
|
||||||
|
container_name: xcontrol-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: "{{ xcontrol_db_name }}"
|
||||||
|
POSTGRES_USER: "{{ xcontrol_db_user }}"
|
||||||
|
POSTGRES_PASSWORD: "{{ xcontrol_db_password }}"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U {{ xcontrol_db_user }}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 60s
|
||||||
|
retries: 10
|
||||||
|
start_period: 5s
|
||||||
|
volumes:
|
||||||
|
- "data:/var/lib/postgresql/data:rw"
|
||||||
|
networks:
|
||||||
|
- db
|
||||||
|
|
||||||
|
account:
|
||||||
|
image: "{{ xcontrol_account_image }}"
|
||||||
|
container_name: account
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
PORT: 8080
|
||||||
|
CONFIG_PATH: /etc/xcontrol/account-compose.yaml
|
||||||
|
volumes:
|
||||||
|
- "{{ xcontrol_workspace }}/config/account.yaml:/etc/xcontrol/account-compose.yaml:ro"
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
- db
|
||||||
|
|
||||||
|
rag-server:
|
||||||
|
image: "{{ xcontrol_rag_image }}"
|
||||||
|
container_name: rag-server
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
PORT: 8090
|
||||||
|
CONFIG_PATH: /etc/rag-server/server-compose.yaml
|
||||||
|
volumes:
|
||||||
|
- "{{ xcontrol_workspace }}/config/server.yaml:/etc/rag-server/server-compose.yaml:ro"
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "8090:8090"
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
- db
|
||||||
|
|
||||||
|
dashboard:
|
||||||
|
image: "{{ xcontrol_dashboard_image }}"
|
||||||
|
container_name: dashboard
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
PORT: 3000
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
depends_on:
|
||||||
|
account:
|
||||||
|
condition: service_started
|
||||||
|
rag-server:
|
||||||
|
condition: service_started
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
|
||||||
|
proxy-external-tls:
|
||||||
|
image: nginx:mainline-alpine
|
||||||
|
container_name: proxy-external-tls
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- "{{ xcontrol_workspace }}/nginx/nginx.conf:/etc/nginx/nginx.conf"
|
||||||
|
- "{{ xcontrol_workspace }}/nginx/conf.d:/etc/nginx/conf.d:ro"
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/conf:/etc/letsencrypt"
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/www:/var/www/certbot"
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
depends_on:
|
||||||
|
account:
|
||||||
|
condition: service_started
|
||||||
|
rag-server:
|
||||||
|
condition: service_started
|
||||||
|
dashboard:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: redis
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["redis-server", "--save", "", "--appendonly", "no"]
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
|
||||||
|
bootstrap-nginx:
|
||||||
|
profiles: ["bootstrap"]
|
||||||
|
image: nginx:mainline-alpine
|
||||||
|
container_name: bootstrap-nginx
|
||||||
|
volumes:
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/www:/var/www/certbot"
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/conf:/etc/letsencrypt"
|
||||||
|
- "{{ xcontrol_workspace }}/nginx/nginx.conf:/etc/nginx/nginx.conf"
|
||||||
|
- "{{ xcontrol_workspace }}/nginx/conf.d/bootstrap-nginx.conf:/etc/nginx/conf.d/default.conf"
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost"]
|
||||||
|
interval: 3s
|
||||||
|
timeout: 2s
|
||||||
|
retries: 10
|
||||||
|
start_period: 3s
|
||||||
|
|
||||||
|
certbot:
|
||||||
|
profiles: ["bootstrap"]
|
||||||
|
image: certbot/certbot
|
||||||
|
container_name: certbot
|
||||||
|
command: >
|
||||||
|
certonly --webroot
|
||||||
|
--webroot-path=/var/www/certbot
|
||||||
|
--email {{ xcontrol_certbot_email }}
|
||||||
|
--agree-tos
|
||||||
|
--no-eff-email
|
||||||
|
--keep-until-expiring
|
||||||
|
--non-interactive
|
||||||
|
-d {{ xcontrol_certbot_domains }}
|
||||||
|
volumes:
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/conf:/etc/letsencrypt"
|
||||||
|
- "{{ xcontrol_workspace }}/certbot/www:/var/www/certbot"
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app:
|
||||||
|
db:
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name {{ xcontrol_accounts_domain }};
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name {{ xcontrol_accounts_domain }};
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
location ^~ /api/auth/ {
|
||||||
|
proxy_pass http://account:8080;
|
||||||
|
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;
|
||||||
|
|
||||||
|
add_header Access-Control-Allow-Origin $cors_origin always;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
|
||||||
|
add_header Access-Control-Allow-Headers "Authorization, Content-Type, Cookie" always;
|
||||||
|
add_header Access-Control-Allow-Credentials "true" always;
|
||||||
|
|
||||||
|
if ($request_method = OPTIONS) {
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate";
|
||||||
|
add_header Pragma "no-cache";
|
||||||
|
add_header Expires "0";
|
||||||
|
|
||||||
|
proxy_cookie_path / "/; Secure; HttpOnly; SameSite=None";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name {{ xcontrol_artifact_domain }} {{ xcontrol_artifact_cn_domain }};
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
root /data/update-server;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location ^~ /.well-known/ { allow all; }
|
||||||
|
|
||||||
|
# ✅ JSON 专用——放在 / 之前
|
||||||
|
location ~* \.json$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
add_header Cache-Control "public, max-age=60, s-maxage=60, stale-while-revalidate=300";
|
||||||
|
default_type application/json;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 目录浏览
|
||||||
|
location / {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_exact_size off;
|
||||||
|
autoindex_localtime on;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 隐藏 dotfiles(不拦 /.well-known/)
|
||||||
|
location ~ /\.(?!well-known/)[^/]+ { deny all; }
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name {{ xcontrol_artifact_domain }} {{ xcontrol_artifact_cn_domain }};
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 200 "bootstrap";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
# ----------------------------------------------------
|
||||||
|
# 80 - ACME Challenge + Redirect to HTTPS for homepage
|
||||||
|
# ----------------------------------------------------
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name {{ xcontrol_homepage_domain }};
|
||||||
|
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# 443 - TLS Termination for homepage
|
||||||
|
# ----------------------------------------------------
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name {{ xcontrol_homepage_domain }};
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/privkey.pem;
|
||||||
|
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://dashboard:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name {{ xcontrol_homepage_alias_domain }} {{ xcontrol_homepage_cn_domain }};
|
||||||
|
|
||||||
|
# Certbot HTTP-01 challenge
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
# All HTTP → HTTPS
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name {{ xcontrol_homepage_alias_domain }} {{ xcontrol_homepage_cn_domain }};
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
# ====== 静态根目录(Next.js export 产物)======
|
||||||
|
root /dashboard/;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# (可选)放行 ACME/健康检查等
|
||||||
|
location ^~ /.well-known/ { allow all; }
|
||||||
|
|
||||||
|
# =======================
|
||||||
|
# API 反向代理(保持原样)
|
||||||
|
# =======================
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://account:8080;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# /api/askai 接口限流(保持原样)
|
||||||
|
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("redis", 6379)
|
||||||
|
if not ok then
|
||||||
|
ngx.log(ngx.ERR, "Redis connect error: ", 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 key = "limit:user:" .. user .. ":" .. today
|
||||||
|
|
||||||
|
local count, err = r:incr(key)
|
||||||
|
if count == 1 then r:expire(key, 86400) end
|
||||||
|
if count > 200 then
|
||||||
|
ngx.status = 429
|
||||||
|
ngx.header["Content-Type"] = "text/plain; charset=utf-8"
|
||||||
|
ngx.say("Too Many Requests: daily limit reached")
|
||||||
|
return ngx.exit(429)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_pass http://account:8080;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# =======================
|
||||||
|
# 静态文件直出(替换原先的 Next.js 动态代理)
|
||||||
|
# =======================
|
||||||
|
|
||||||
|
# Next 导出的静态资源(hash 不变 -> 长缓存)
|
||||||
|
location ^~ /_next/static/ {
|
||||||
|
try_files $uri =404;
|
||||||
|
access_log off;
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable, max-age=31536000";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 其他常见静态资源:中等缓存
|
||||||
|
location ~* \.(?:js|css|png|jpg|jpeg|gif|svg|webp|ico|woff2?|ttf)$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
access_log off;
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public, max-age=604800";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主页与已导出的所有路由:按文件/目录匹配
|
||||||
|
# 未命中的交给 404.html(保持静态站语义)
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显式处理 404/500 路由目录(Next export 会生成 404/、500/ 与同名 .html)
|
||||||
|
location = /404.html { internal; }
|
||||||
|
error_page 404 /404.html;
|
||||||
|
|
||||||
|
# 如果有 /favicon.ico,则直接给文件
|
||||||
|
location = /favicon.ico {
|
||||||
|
try_files /favicon.ico =204;
|
||||||
|
access_log off;
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, max-age=2592000";
|
||||||
|
}
|
||||||
|
|
||||||
|
# (可选)为某些目录开启目录索引(你有 dl-index、docs、download)
|
||||||
|
# 若需要列表页可以这样做;不需要则删除本段
|
||||||
|
location ^~ /dl-index/ {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_exact_size off;
|
||||||
|
autoindex_localtime on;
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 拒绝访问隐藏文件(如 .env)
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# (可选)开启 gzip(如启用 ngx_brotli,也可再加 br)
|
||||||
|
gzip on;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
gzip_min_length 1k;
|
||||||
|
gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;
|
||||||
|
gzip_vary on;
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name {{ xcontrol_rag_domain }} {{ xcontrol_rag_api_domain }};
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name {{ xcontrol_rag_domain }} {{ xcontrol_rag_api_domain }};
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ xcontrol_primary_domain }}/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
location ^~ /api/ {
|
||||||
|
proxy_pass http://rag-server:8090;
|
||||||
|
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;
|
||||||
|
|
||||||
|
add_header Access-Control-Allow-Origin $cors_origin always;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
|
||||||
|
add_header Access-Control-Allow-Headers "Authorization, Content-Type, Cookie" always;
|
||||||
|
add_header Access-Control-Allow-Credentials "true" always;
|
||||||
|
|
||||||
|
if ($request_method = OPTIONS) {
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_header Cache-Control "no-store";
|
||||||
|
}
|
||||||
|
|
||||||
|
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("redis", 6379)
|
||||||
|
if not ok then
|
||||||
|
ngx.log(ngx.ERR, "Redis connect error: ", 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 key = "limit:user:" .. user .. ":" .. today
|
||||||
|
|
||||||
|
local count, err = r:incr(key)
|
||||||
|
if count == 1 then r:expire(key, 86400) end
|
||||||
|
if count > 200 then
|
||||||
|
ngx.status = 429
|
||||||
|
ngx.header["Content-Type"] = "text/plain; charset=utf-8"
|
||||||
|
ngx.say("Too Many Requests: daily limit reached")
|
||||||
|
return ngx.exit(429)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_pass http://rag-server:8090;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user