Browse Source

feat: 增加fastcgi配置

tuon 1 year ago
parent
commit
8a42009b2c

+ 14 - 0
src/config/nginx_form.json

@@ -65,6 +65,13 @@
       "required": false,
       "description": "更多代理设置"
     },
+    {
+      "key": "fastcgi",
+      "title": "fastcgi",
+      "type": "fastcgi",
+      "required": false,
+      "description": "ngx_http_fastcgi_module,allows passing requests to a FastCGI server."
+    },
     {
       "type": "locations",
       "title": "代理/站点",
@@ -473,6 +480,13 @@
           "type": "proxy_settings",
           "required": false
         },
+        {
+          "key": "http.fastcgi",
+          "title": "fastcgi",
+          "type": "fastcgi",
+          "required": false,
+          "description": "ngx_http_fastcgi_module,allows passing requests to a FastCGI server."
+        },
         {
           "key": "http.more",
           "type": "textarea",

+ 18 - 0
src/pages/nginx/components/basic/index.less

@@ -17,3 +17,21 @@
     }
   }
 }
+
+.drawer-input{
+  .ant-drawer-header{
+    padding: 5px 15px;
+  }
+  .ant-drawer-body{
+    padding: 15px;
+    .form-btns{
+      padding-left: 16%;
+      .ant-btn+.ant-btn{
+        margin-left: 10px;
+      }
+    }
+  }
+  .ant-drawer-content-wrapper{
+
+  }
+}

+ 36 - 11
src/pages/nginx/components/basic/index.tsx

@@ -3,12 +3,13 @@
  * @date 2023/7/5
  */
 import {AdvanceInputConfigs, AutoForm, AutoTypeInputProps, FormColumnType} from 'planning-tools'
-import {Button, FormInstance, Popover} from "antd";
+import {Button, Drawer, FormInstance, Popover} from "antd";
 import {EditOutlined} from "@ant-design/icons";
 import React, {useEffect, useState} from "react";
 import {AutoFormFooterProps} from "planning-tools/dist/esm/Components/AutoForm/form";
 import {NgxModuleData} from "../input.ts";
 import './index.less'
+import {DrawerProps} from "antd/lib/drawer";
 
 export type OnChange = (values: any) => void
 
@@ -23,7 +24,9 @@ type IProps = {
   renderLines: (values: any) => string[]
   renderHttpLines?: (values: any) =>string[]
   overlayClassName?: string
-  labelCol?: number
+  labelCol?: number;
+  drawer?: boolean;
+  drawerProps?: Partial<DrawerProps>
 }
 export const NgxBasicInput = (
   {
@@ -33,7 +36,9 @@ export const NgxBasicInput = (
       renderHttpLines,
     overlayClassName,
     labelCol = 6,
-    value, onChange
+    value, onChange,
+      drawer,
+      drawerProps,
   }: IProps & AutoTypeInputProps) => {
 
   const [data, setData] = useState<any>({})
@@ -87,16 +92,36 @@ export const NgxBasicInput = (
                       data={data}/>)
   }
 
+  const renderFormContainer = ()=>{
+      if (drawer){
+          return (<>
+              <Button type="link" onClick={() => setOpen(true)} icon={<EditOutlined/>}/>
+              <Drawer open={open}
+                      destroyOnClose
+                      onClose={()=>setOpen(false)}
+                      width={500}
+                      className="drawer-input"
+                      {...drawerProps}
+              >
+                  {renderForm()}
+              </Drawer>
+              </>)
+      }
+      return (
+          <Popover destroyTooltipOnHide
+                   overlayClassName={`popover-popover ${overlayClassName || ''}`}
+                   placement="right"
+                   open={open}
+                   onOpenChange={o => setOpen(o)}
+                   trigger="click" content={renderForm}>
+              <Button type="link" onClick={() => setOpen(true)} icon={<EditOutlined/>}/>
+          </Popover>
+      )
+  }
+
   return (<div className="popover-input">
     <ContentComp data={data} onChange={onValuesChange}/>
-    <Popover destroyTooltipOnHide
-             overlayClassName={`popover-popover ${overlayClassName || ''}`}
-             placement="right"
-             open={open}
-             onOpenChange={o => setOpen(o)}
-             trigger="click" content={renderForm}>
-      <Button type="link" onClick={() => setOpen(true)} icon={<EditOutlined/>}/>
-    </Popover>
+      {renderFormContainer()}
   </div>)
 }
 

+ 145 - 0
src/pages/nginx/components/fastcgi/config.json

@@ -0,0 +1,145 @@
+{
+  "form": [
+    {
+      "title": "fastcgi_pass",
+      "key": "fastcgi_pass",
+      "type": "proxy_pass",
+      "hideProtocol": true,
+      "required": false,
+      "description": "设置 FastCGI 服务器的地址。该地址可以指定为域名或 IP 地址,以及端口或者作为 UNIX 域套接字路径。如果域名解析为多个地址,则所有这些地址都将以循环方式使用;<br>参考来源:https://blog.51cto.com/u_15715098/5733155"
+    },
+    {
+      "title": "index",
+      "key": "fastcgi_index",
+      "type": "select",
+      "option": ["index.php"],
+      "mode": "tags",
+      "required": false
+    },
+    {
+      "title": "缓存路径",
+      "key": "fastcgi_cache_path",
+      "type": "string",
+      "width": 460,
+      "required": false,
+      "description": "设置缓存的路径和其他参数。缓存数据存储在文件中。缓存中的 key 和文件名是代理 URL 经过 MD5 函数处理后得到的值。levels参数定义缓存的层次结构级别:范围从1到3,每个级别可接受值为1或2。eg. <br> fastcgi_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m"
+    },
+    {
+      "title": "缓冲区大小",
+      "key": "fastcgi_buffer_size",
+      "type": "string",
+      "ruleType": "reg",
+      "pattern": "^(\\d+)k$",
+      "description":"指定读取FastCGI应答第一部分需要用多大的缓冲区,一般第一部分应答不会超过1k,由于页面大小为4k,所以这里设置为4k。",
+      "required": false,
+      "value": "4k"
+    },
+    {
+      "title": "缓冲设置",
+      "key": "fastcgi_buffers",
+      "type": "string",
+      "ruleType": "reg",
+      "pattern": "^(\\d+)(\\s+)(\\d+k)$",
+      "description":"指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答,eg. 8 4k",
+      "required": false,
+      "value": "8 4k"
+    },
+    {
+      "title": "fastcgi_bind",
+      "type": "object",
+      "key": "fastcgi_bind",
+      "required": false,
+      "hideHeader": false,
+      "description": "从指定的本地IP地址发出到 FastCGI 服务器的传出连接。特殊值off取消从上层配置级别继承到的fastcgi_bind指令作用,这允许系统自动分配本地IP地址和端口。",
+      "items": [
+        {
+          "title": "address",
+          "key": "address",
+          "type": "string",
+          "width": 250,
+          "required": false,
+          "description": "IP or off or $remote_addr"
+        },
+        {
+          "title": "transparent",
+          "key": "transparent",
+          "type": "switch",
+          "width": 120,
+          "description": "允许从非本地 IP 地址(例如来自客户端的真实 IP 地址)的到 FastCGI 服务器的传出连接,比如:$remote_addr"
+        }
+      ]
+    },
+    {
+      "title": "参数设置",
+      "type": "array",
+      "key": "fastcgi_param",
+      "hideHeader": true,
+      "required": false,
+      "description": "设置应传递给 FastCGI 服务器的parameter(参数)。该值可以包含文本、变量及其组合。当且仅当在当前级别上没有定义fastcgi_param指令时,这些指令才从前一级继承。",
+      "items": [
+        {
+          "title": "参数名",
+          "type": "string",
+          "width": 150,
+          "key": "name"
+        },
+        {
+          "title": "参数值",
+          "type": "string",
+          "width": 250,
+          "key": "value"
+        }
+      ]
+    },
+    {
+      "title": "连接超时时间",
+      "key": "fastcgi_connect_timeout",
+      "type": "string",
+      "ruleType": "reg",
+      "pattern": "^(\\d+)(s|m)?$",
+      "description":"设置与 FastCGI 服务器建立连接的超时时间。需要注意的是,这个超时通常不能超过 75 秒。",
+      "required": false
+    },
+    {
+      "title": "发送超时时间",
+      "key": "fastcgi_send_timeout",
+      "type": "string",
+      "ruleType": "reg",
+      "pattern": "^(\\d+)(s|m)?$",
+      "description":"向FastCGI传送请求的超时时间,这个值是指已经完成两次握手后向FastCGI传送请求的超时时间。",
+      "required": false
+    },
+    {
+      "title": "接收超时时间",
+      "key": "fastcgi_read_timeout",
+      "type": "string",
+      "ruleType": "reg",
+      "pattern": "^(\\d+)(s|m)?$",
+      "description":"接收FastCGI应答的超时时间,这个值是指已经完成两次握手后接收FastCGI应答的超时时间",
+      "required": false
+    },
+    {
+      "title": "更多配置",
+      "type": "array",
+      "key": "more_settings",
+      "hideHeader": true,
+      "required": false,
+      "items": [
+        {
+          "title": "名称",
+          "type": "string",
+          "width": 150,
+          "key": "name",
+          "ruleType": "^(fastcgi)_(.+)$",
+          "description": "fastcgi_开头的配置名称,eq. fastcgi_cache_min_uses"
+        },
+        {
+          "title": "配置",
+          "type": "string",
+          "width": 250,
+          "key": "value"
+        }
+      ]
+    }
+  ]
+}

+ 30 - 0
src/pages/nginx/components/fastcgi/index.less

@@ -0,0 +1,30 @@
+.ngx-fastcgi-input{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  .less-values{
+    max-width: 250px;
+    overflow: hidden;
+    display: inline-block;
+    line-height: 32px;
+    height: 32px;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+}
+
+
+
+.ngx-fastcgi-popover{
+  .ant-popover-inner-content{
+    width: 100%;
+    padding: 5px;
+  }
+  .access-more-values.ant-input{
+    min-width: 450px;
+    color: #333333;
+    border: none;
+    cursor: text;
+  }
+
+}

+ 80 - 0
src/pages/nginx/components/fastcgi/index.tsx

@@ -0,0 +1,80 @@
+/**
+ * @author tuonian
+ * @date 2023/7/31
+ */
+import {IContentProps, NgxBasicInput, registerInput} from "../basic";
+import {AutoTypeInputProps} from "planning-tools";
+
+import CFG from './config.json'
+import {Input, Popover} from "antd";
+import './index.less'
+import {renderInputLines} from "../utils";
+import {KeyValue, ProcessorData} from "../input.ts";
+
+/**
+ * 部分特殊字段的渲染
+ */
+const CustomRender = ({key, value, lines,}: ProcessorData) => {
+    if (key ==='fastcgi_param' || key === 'more_settings'){
+        (value as KeyValue[]).forEach(item=>{
+            lines.push(`fastcgi_param  ${item.name}  ${item.value};`)
+        })
+        return true;
+    }
+    if (key === 'fastcgi_bind'){
+        if (!value.address){
+           return true
+        }
+        if (value.address =='off'){
+            lines.push(`fastcgi_bind  off;`);
+        }else {
+            lines.push(`fastcgi_bind  ${value.address}  ${value.transparent ? 'transparent': ''};`)
+        }
+        return true
+    }
+    return false
+}
+
+export const FastcgiInput = (props: AutoTypeInputProps)=>{
+
+    const renderMoreContent = (lines?: string[])=>{
+        if (!lines?.length){
+            return <span>无配置</span>
+        }
+        return (<Input.TextArea className="access-more-values"
+                                rows={Math.min(10,lines.length)}
+                                disabled value={lines.join('\n')} />)
+    }
+
+    const renderContent = (props: IContentProps) => {
+
+        const lines = renderLines(props.data)
+
+        return (
+            <div className="ngx-fastcgi-input">
+                <Popover
+                    overlayClassName="ngx-fastcgi-popover"
+                    destroyTooltipOnHide content={()=>renderMoreContent(lines)}>
+                    <span className="less-values">{lines.length ? lines.join(' ') : '无配置'}</span>
+                </Popover>
+            </div>
+        )
+    }
+
+    const renderLines = (values: any)=>{
+        const result = renderInputLines(values, CustomRender)
+        return result.lines;
+    }
+
+    return <NgxBasicInput renderLines={renderLines}
+                          columns={CFG.form}
+                          labelCol={4}
+                          drawer={true}
+                          drawerProps={{
+                              width: 650
+                          }}
+                          content={renderContent}
+                          {...props}/>
+}
+
+registerInput('fastcgi',FastcgiInput)

+ 1 - 0
src/pages/nginx/components/index.ts

@@ -8,3 +8,4 @@ import './proxypass/stream.tsx'
 import './error'
 import './cors'
 import './access'
+import './fastcgi'

+ 28 - 0
src/pages/nginx/components/input.ts

@@ -26,3 +26,31 @@ export const isNgxModuleValue = (value: any)=>{
   }
   return !!Array.isArray((value as NgxModuleData)?.lines);
 }
+
+/**
+ * 键值对
+ */
+export type KeyValue = {
+    name: string
+    value: string
+}
+
+/**
+ * 值是那种 ,{name: xxx, value: 123}的形式
+ * @param value
+ */
+export const isNameValue = (value: any)=>{
+    return value && value.name && value.value
+}
+
+export type ProcessorData = {
+    key: string,
+    value: any,
+    lines:string[],
+    httpLines:string[]
+}
+
+/**
+ * 返回true,表示已经处理完了,不继续后续的处理
+ */
+export type IRenderProcessor = (data: ProcessorData) => boolean

+ 15 - 1
src/pages/nginx/components/location/config.json

@@ -70,7 +70,12 @@
       },{
         "label": "静态资源",
         "value": "static"
-      },{
+      },
+        {
+          "label": "fastcgi",
+          "value": "fastcgi"
+        },
+        {
         "label": "其它",
         "value": "other"
       }],
@@ -124,6 +129,15 @@
             "width": 450
           }
         ],
+        "fastcgi": [
+          {
+            "key": "fastcgi",
+            "title": "fastcgi",
+            "type": "fastcgi",
+            "required": false,
+            "description": "ngx_http_fastcgi_module,allows passing requests to a FastCGI server."
+          }
+        ],
         "other": [
           {
             "key": "return",

+ 26 - 7
src/pages/nginx/components/proxypass/index.tsx

@@ -11,14 +11,24 @@ import './index.less'
 
 const protocols = [{value:"http", label:"http"},{ value: 'https',label: 'https'}]
 
-
-export const ProxyPassInput = ({value, onChange}: AutoTypeInputProps)=>{
+/**
+ * proxy_pass 或者fastcgi_pass .后者没有协议
+ * @param value
+ * @param onChange
+ * @param column
+ * @constructor
+ */
+export const ProxyPassInput = ({value, onChange, column}: AutoTypeInputProps)=>{
 
   const upstreamServer = useAppSelector(state => state.nginx.upstream);
 
   const [data,setData] = useState<string>()
   const [protocol,setProtocol] = useState<string>("http")
 
+    const hideProtocol = useMemo(()=>{
+        return (column as any).hideProtocol;
+    },[column])
+
   const options = useMemo(()=>{
     let list:any[] = []
     if (upstreamServer?.upstreams){
@@ -52,8 +62,12 @@ export const ProxyPassInput = ({value, onChange}: AutoTypeInputProps)=>{
     if (!pro || !host){
       return
     }
-    const pass = `${pro}://${host}`
-    onChange?.(pass)
+    if (hideProtocol){
+        onChange?.(host)
+    }else {
+        const pass = `${pro}://${host}`
+        onChange?.(pass)
+    }
   }
 
   const onProtocolChange = (pro: string)=>{
@@ -63,7 +77,7 @@ export const ProxyPassInput = ({value, onChange}: AutoTypeInputProps)=>{
 
   const onSelectUpstream = (v?:string)=>{
     if (v){
-      const val = v +'/'
+      const val = v + (hideProtocol ? '': '/')
       setData(val)
       triggerChange(protocol,val)
     }
@@ -78,8 +92,13 @@ export const ProxyPassInput = ({value, onChange}: AutoTypeInputProps)=>{
 
 
   return (<div className="proxy-pass-input">
-    <Select value={protocol} onChange={onProtocolChange}
-            className="protocol" options={protocols} />
+      {
+          hideProtocol ? null: (
+              <Select value={protocol}
+                      onChange={onProtocolChange}
+                      className="protocol" options={protocols} />
+          )
+      }
     <Input onChange={userInputChange} value={data} allowClear/>
     <Select onChange={onSelectUpstream}
             placeholder="选择负载均衡"

+ 76 - 0
src/pages/nginx/components/utils/index.ts

@@ -0,0 +1,76 @@
+import {isBasicData, isNull} from "planning-tools";
+import {IRenderProcessor, isNameValue, isNgxModuleValue, NgxModuleData, KeyValue, ProcessorData} from "../input.ts";
+import {isBoolean} from "lodash";
+
+/**
+ * 渲染基础类型的数据,int,string,bool
+ * @param lines
+ * @param k
+ * @param value
+ */
+export const renderBasicData = (lines: string[], k: string, value: any)=>{
+    if (!isBasicData(value)){
+        return false
+    }
+    if (isBoolean(value)){
+        value = value ? 'on': 'off'
+    }
+    lines.push(`${k}      ${value};`)
+    return true
+}
+
+
+/**
+ * 自定义输入项的通用渲染规则,
+ * @param values
+ * @param processor 自定义渲染方法,仅能处理一级,别搞多
+ */
+export const renderInputLines = (values: any, processor?: IRenderProcessor) => {
+    console.log('renderLines',values)
+    const lines: string[] = [];
+    const httpLines:string[] = [];
+    Object.keys(values).forEach(k=>{
+        let value = values[k];
+        if (isNull(value)){
+            return;
+        }
+        const processData: ProcessorData = {
+            lines,
+            httpLines,
+            key: k,
+            value: value
+        }
+        if (processor?.(processData)){
+            return;
+        }
+        if (isNgxModuleValue(value)){
+            const ngxData = value as NgxModuleData
+            ngxData.lines?.forEach((item: string)=>lines.push(`${item}`))
+            ngxData.http?.forEach(line=>httpLines.push(line))
+            return;
+        }
+
+        if (renderBasicData(lines,k,value)){
+            return;
+        }
+        if (Array.isArray(value) && value.length){
+            const firstValue = value[0]
+            if (isNgxModuleValue(firstValue)){
+                value.forEach((data: NgxModuleData)=>{
+                    data.http?.forEach(line=>httpLines.push(line))
+                    data.lines?.forEach(line=>lines.push(`${line}`))
+                })
+            }else if (isNameValue(firstValue)){
+                value.forEach((data: KeyValue)=>{
+                    lines.push(`${data.name}      ${data.value};`)
+                })
+            }else if (isBasicData(firstValue)){
+                value = value.join(' ')
+                lines.push(`${k}      ${value};`)
+            }
+        }else {
+            console.warn('data format not valid', k, value)
+        }
+    })
+    return { lines, httpLines}
+}