Browse Source

[frontend-next] Refactored NgxConfigEditor

0xJacky 2 năm trước cách đây
mục cha
commit
0d4b126dd4

+ 5 - 0
frontend-next/components.d.ts

@@ -15,9 +15,11 @@ declare module '@vue/runtime-core' {
     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
     ADivider: typeof import('ant-design-vue/es')['Divider']
     ADrawer: typeof import('ant-design-vue/es')['Drawer']
+    AEmpty: typeof import('ant-design-vue/es')['Empty']
     AForm: typeof import('ant-design-vue/es')['Form']
     AFormItem: typeof import('ant-design-vue/es')['FormItem']
     AInput: typeof import('ant-design-vue/es')['Input']
+    AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
     AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
     ALayout: typeof import('ant-design-vue/es')['Layout']
     ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
@@ -38,7 +40,10 @@ declare module '@vue/runtime-core' {
     ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
     ASwitch: typeof import('ant-design-vue/es')['Switch']
     ATable: typeof import('ant-design-vue/es')['Table']
+    ATabPane: typeof import('ant-design-vue/es')['TabPane']
+    ATabs: typeof import('ant-design-vue/es')['Tabs']
     ATag: typeof import('ant-design-vue/es')['Tag']
+    ATextarea: typeof import('ant-design-vue/es')['Textarea']
     Breadcrumb: typeof import('./src/components/Breadcrumb/Breadcrumb.vue')['default']
     CodeEditor: typeof import('./src/components/CodeEditor/CodeEditor.vue')['default']
     FooterToolBar: typeof import('./src/components/FooterToolbar/FooterToolBar.vue')['default']

+ 7 - 2
frontend-next/src/components/CodeEditor/CodeEditor.vue

@@ -3,7 +3,10 @@ import {VAceEditor} from 'vue3-ace-editor'
 import 'ace-builds/src-noconflict/mode-nginx'
 import 'ace-builds/src-noconflict/theme-monokai'
 
-const {content} = defineProps(['content'])
+const {content, defaultHeight} = defineProps<{
+    content?: string
+    defaultHeight?: string
+}>()
 </script>
 
 <template>
@@ -11,7 +14,9 @@ const {content} = defineProps(['content'])
         v-model:value="content"
         lang="nginx"
         theme="monokai"
-        style="min-height: 100vh"/>
+        :style="{
+            minHeight: defaultHeight || '100vh'
+        }"/>
 </template>
 
 <style scoped>

+ 3 - 0
frontend-next/src/components/CodeEditor/index.ts

@@ -0,0 +1,3 @@
+import CodeEditor from './CodeEditor'
+
+export default CodeEditor

+ 8 - 8
frontend-next/src/views/domain/DomainEdit.vue

@@ -2,7 +2,7 @@
 import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
 import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
 
-// import NgxConfigEditor from '@/views/domain/ngx_conf/NgxConfigEditor'
+import NgxConfigEditor from '@/views/domain/ngx_conf/NgxConfigEditor'
 import {useGettext} from 'vue3-gettext'
 import {reactive, ref} from 'vue'
 import {useRoute} from 'vue-router'
@@ -17,6 +17,7 @@ const route = useRoute()
 
 const name = ref(route.params.name.toString())
 const update = ref(0)
+
 const ngx_config = reactive({
     filename: '',
     upstreams: [],
@@ -141,13 +142,12 @@ function disable() {
                     <a-form-item :label="$gettext('Enabled')">
                         <a-switch v-model="enabled" @change="checked=>{checked?enable():disable()}"/>
                     </a-form-item>
-
-                    <!--                    <ngx-config-editor-->
-                    <!--                        ref="ngx_config"-->
-                    <!--                        :ngx_config="ngx_config"-->
-                    <!--                        v-model="auto_cert"-->
-                    <!--                        :enabled="enabled"-->
-                    <!--                    />-->
+                    <ngx-config-editor
+                        ref="ngx_config_editor"
+                        :ngx_config="ngx_config"
+                        v-model:auto_cert="auto_cert"
+                        :enabled="enabled"
+                    />
                 </div>
             </transition>
 

+ 29 - 42
frontend-next/src/views/domain/cert/CertInfo.vue

@@ -1,61 +1,48 @@
+<script setup lang="ts">
+import {CloseCircleOutlined, CheckCircleOutlined} from '@ant-design/icons-vue'
+import dayjs from 'dayjs'
+import {defineProps, reactive, ref} from 'vue'
+import domain from '@/api/domain'
+
+const props = defineProps(['domain'])
+
+const ok = ref(false)
+const cert = reactive({issuer_name: '', subject_name: '', not_after: '', not_before: ''})
+
+get()
+
+function get() {
+    domain.cert_info(props.domain).then((r: any) => {
+        Object.assign(cert, r)
+        ok.value = true
+    }).catch(() => {
+        ok.value = false
+    })
+}
+</script>
+
 <template>
     <div class="cert-info" v-if="ok">
-        <h4 v-translate>Certificate Status</h4>
+        <h2 v-translate>Certificate Status</h2>
         <p v-translate="{issuer: cert.issuer_name}">Intermediate Certification Authorities: %{issuer}</p>
         <p v-translate="{name: cert.subject_name}">Subject Name: %{name}</p>
-        <p v-translate="{date: moment(cert.not_after).format('YYYY-MM-DD HH:mm:ss').toString()}">
+        <p v-translate="{date: dayjs(cert.not_after).format('YYYY-MM-DD HH:mm:ss').toString()}">
             Expiration Date: %{date}</p>
-        <p v-translate="{date: moment(cert.not_before).format('YYYY-MM-DD HH:mm:ss').toString()}">
+        <p v-translate="{date: dayjs(cert.not_before).format('YYYY-MM-DD HH:mm:ss').toString()}">
             Not Valid Before: %{date}</p>
         <div class="status">
             <template v-if="new Date().toISOString() < cert.not_before || new Date().toISOString() > cert.not_after">
-                <a-icon :style="{ color: 'red' }" type="close-circle"/>
+                <close-circle-outlined style="color: red"/>
                 <span v-translate>Certificate has expired</span>
             </template>
             <template v-else>
-                <a-icon :style="{ color: 'green' }" type="check-circle"/>
+                <check-circle-outlined style="color: green"/>
                 <span v-translate>Certificate is valid</span>
             </template>
         </div>
     </div>
 </template>
 
-<script>
-import moment from 'moment'
-
-export default {
-    name: 'CertInfo',
-    data() {
-        return {
-            ok: false,
-            cert: {},
-            moment
-        }
-    },
-    props: {
-        domain: String
-    },
-    created() {
-        this.get()
-    },
-    watch: {
-        domain() {
-            this.get()
-        }
-    },
-    methods: {
-        get() {
-            this.$api.domain.cert_info(this.domain).then(r => {
-                this.cert = r
-                this.ok = true
-            }).catch(() => {
-                this.ok = false
-            })
-        }
-    }
-}
-</script>
-
 <style lang="less" scoped>
 h4 {
     padding-bottom: 10px;
@@ -67,7 +54,7 @@ h4 {
 
 .status {
     span {
-        margin-left: 10px;
+        margin-right: 10px;
     }
 }
 

+ 58 - 64
frontend-next/src/views/domain/ngx_conf/LocationEditor.vue

@@ -1,75 +1,69 @@
-<template>
-    <a-form-item :label="$gettext('Locations')" :key="update">
-        <a-empty v-if="!locations"/>
-        <a-card v-for="(v,k) in locations" :key="k"
-                :title="$gettext('Location')" size="small">
-            <a-form-item :label="$gettext('Comments')" v-if="v.comments">
-                <p style="white-space: pre-wrap;">{{ v.comments }}</p>
-            </a-form-item>
-            <a-form-item :label="$gettext('Path')">
-                <a-input addon-before="location" v-model="v.path"/>
-            </a-form-item>
-            <a-form-item :label="$gettext('Content')">
-                <vue-itextarea v-model="v.content" :default-text-height="200"/>
-            </a-form-item>
-        </a-card>
+<script setup lang="ts">
+import CodeEditor from '@/components/CodeEditor'
+import {useGettext} from 'vue3-gettext'
+import {reactive} from 'vue'
 
-        <a-modal :title="$gettext('Add Location')" v-model="adding" @ok="save">
-            <a-form-item :label="$gettext('Comments')">
-                <a-textarea v-model="location.comments"></a-textarea>
-            </a-form-item>
-            <a-form-item :label="$gettext('Path')">
-                <a-input addon-before="location" v-model="location.path"/>
-            </a-form-item>
-            <a-form-item :label="$gettext('Content')">
-                <vue-itextarea v-model="location.content" :default-text-height="200"/>
-            </a-form-item>
-        </a-modal>
+const {$gettext} = useGettext()
 
-        <div>
-            <a-button block @click="add">{{ $gettext('Add Location') }}</a-button>
-        </div>
-    </a-form-item>
-</template>
+const {locations} = defineProps<{
+    locations?: any[]
+}>()
+
+let location = reactive({})
 
-<script>
-import VueItextarea from '@/components/VueItextarea/VueItextarea'
 
-export default {
-    name: 'LocationEditor',
-    components: {VueItextarea},
-    props: {
-        locations: Array
-    },
-    data() {
-        return {
-            adding: false,
-            location: {},
-            update: 0
-        }
-    },
-    methods: {
-        add() {
-            this.adding = true
-            this.location = {}
-        },
-        save() {
-            this.adding = false
-            if (this.locations) {
-                this.locations.push(this.location)
-            } else {
-                this.locations = [this.location]
-            }
-            this.update++
-        },
-        remove(index) {
-            this.update++
-            this.locations.splice(index, 1)
-        }
+function add() {
+    adding.value = true
+    location = reactive({})
+}
+
+function save() {
+    this.adding = false
+    if (this.locations) {
+        this.locations.push(this.location)
+    } else {
+        this.locations = [this.location]
     }
 }
+
+function remove(index) {
+    this.locations.splice(index, 1)
+}
 </script>
 
+<template>
+    <h2 v-translate>Locations</h2>
+    <a-empty v-if="!locations"/>
+    <a-card v-for="(v,k) in locations" :key="k"
+            :title="$gettext('Location')" size="small">
+        <a-form-item :label="$gettext('Comments')" v-if="v.comments">
+            <p style="white-space: pre-wrap;">{{ v.comments }}</p>
+        </a-form-item>
+        <a-form-item :label="$gettext('Path')">
+            <a-input addon-before="location" v-model="v.path"/>
+        </a-form-item>
+        <a-form-item :label="$gettext('Content')">
+            <code-editor v-model:content="v.content" default-height="200px"/>
+        </a-form-item>
+    </a-card>
+
+    <a-modal :title="$gettext('Add Location')" v-model:visible="adding" @ok="save">
+        <a-form-item :label="$gettext('Comments')">
+            <a-textarea v-model="location.comments"></a-textarea>
+        </a-form-item>
+        <a-form-item :label="$gettext('Path')">
+            <a-input addon-before="location" v-model="location.path"/>
+        </a-form-item>
+        <a-form-item :label="$gettext('Content')">
+            <vue-itextarea v-model:content="location.content" default-height="200px"/>
+        </a-form-item>
+    </a-modal>
+
+    <div>
+        <a-button block @click="add">{{ $gettext('Add Location') }}</a-button>
+    </div>
+</template>
+
 <style lang="less" scoped>
 .ant-card {
     margin: 10px 0;

+ 141 - 154
frontend-next/src/views/domain/ngx_conf/NgxConfigEditor.vue

@@ -1,30 +1,158 @@
+<script setup lang="ts">
+import CertInfo from '@/views/domain/cert/CertInfo'
+// import IssueCert from '@/views/domain/cert/IssueCert'
+import DirectiveEditor from '@/views/domain/ngx_conf/directive/DirectiveEditor'
+import LocationEditor from '@/views/domain/ngx_conf/LocationEditor'
+import {computed, ref} from 'vue'
+import {useRoute} from 'vue-router'
+import {useGettext} from 'vue3-gettext'
+
+const {$gettext} = useGettext()
+
+const {ngx_config, auto_cert, enabled} = defineProps(['ngx_config', 'auto_cert', 'enabled'])
+
+const route = useRoute()
+
+const current_server_index = ref(0)
+const name = ref(route.params.name)
+
+const init_ssl_status = ref(false)
+
+function update_cert_info() {
+    // if (name.value && this.$refs['cert-info' + this.current_server_index]) {
+    //     this.$refs['cert-info' + this.current_server_index].get()
+    // }
+}
+
+function change_tls(r: any) {
+    if (r) {
+        // deep copy servers[0] to servers[1]
+        const server = JSON.parse(JSON.stringify(ngx_config.servers[0]))
+
+        ngx_config.servers.push(server)
+
+        current_server_index.value = 1
+
+        const servers = ngx_config.servers
+
+        let i = 0
+        while (i < servers[1].directives.length) {
+            const v = servers[1].directives[i]
+            if (v.directive === 'listen') {
+                servers[1].directives.splice(i, 1)
+            } else {
+                i++
+            }
+        }
+
+        servers[1].directives.splice(0, 0, {
+            directive: 'listen',
+            params: '443 ssl http2'
+        }, {
+            directive: 'listen',
+            params: '[::]:443 ssl http2'
+        })
+
+        const server_name = directivesMap.value['server_name'][0]
+
+        if (!directivesMap.value['ssl_certificate']) {
+            servers[1].directives.splice(server_name.idx + 1, 0, {
+                directive: 'ssl_certificate',
+                params: ''
+            })
+        }
+
+        setTimeout(() => {
+            if (!directivesMap.value['ssl_certificate_key']) {
+                servers[1].directives.splice(server_name.idx + 2, 0, {
+                    directive: 'ssl_certificate_key',
+                    params: ''
+                })
+            }
+        }, 100)
+
+    } else {
+        // remove servers[1]
+        current_server_index.value = 0
+        if (ngx_config.servers.length === 2) {
+            ngx_config.servers.splice(1, 1)
+        }
+    }
+}
+
+const current_server_directives = computed(() => {
+    return ngx_config.servers[current_server_index.value].directives
+})
+
+const directivesMap = computed(() => {
+    const map = <any>{}
+
+    current_server_directives.value.forEach((v: any, k: any) => {
+        v.idx = k
+        if (map[v.directive]) {
+            map[v.directive].push(v)
+        } else {
+            map[v.directive] = [v]
+        }
+    })
+
+    return map
+})
+
+
+const support_ssl = computed(() => {
+    const servers = ngx_config.servers
+    for (const server_key in servers) {
+        for (const k in servers[server_key].directives) {
+            const v = servers[server_key].directives[k]
+            if (v.directive === 'listen' && v.params.indexOf('ssl') > 0) {
+                return true
+            }
+        }
+    }
+    return false
+})
+
+
+const current_support_ssl = computed(() => {
+    if (directivesMap.value.listen) {
+        for (const v of directivesMap.value.listen) {
+            if (v?.params.indexOf('ssl') > 0) {
+                return true
+            }
+        }
+    }
+    return false
+
+})
+
+</script>
+
 <template>
     <div>
         <a-form-item :label="$gettext('Enable TLS')" v-if="!support_ssl">
             <a-switch @change="change_tls"/>
         </a-form-item>
 
-        <a-tabs v-model="current_server_index">
+        <a-tabs v-model:activeKey="current_server_index">
             <a-tab-pane :tab="'Server '+(k+1)" v-for="(v,k) in ngx_config.servers" :key="k">
 
                 <div class="tab-content">
                     <template v-if="current_support_ssl&&enabled">
-                        <cert-info :domain="name" v-if="name"/>
-                        <issue-cert
-                            :current_server_directives="current_server_directives"
-                            :directives-map="directivesMap"
-                            v-model="auto_cert"
-                        />
-                        <cert-info :current_server_directives="current_server_directives"
-                                   :directives-map="directivesMap"
-                                   v-model="auto_cert"/>
+                        <cert-info :domain="name" v-if="current_support_ssl"/>
+                        <!--                        <issue-cert-->
+                        <!--                            :current_server_directives="current_server_directives"-->
+                        <!--                            :directives-map="directivesMap"-->
+                        <!--                            v-model="auto_cert"-->
+                        <!--                        />-->
                     </template>
 
-                    <a-form-item :label="$gettext('Comments')" v-if="v.comments">
+                    <template v-if="v.comments">
+                        <h3 v-translate>Comments</h3>
                         <p style="white-space: pre-wrap;">{{ v.comments }}</p>
-                    </a-form-item>
+                    </template>
 
-                    <directive-editor :ngx_directives="v.directives" :key="update"/>
+                    <directive-editor :ngx_directives="v.directives"/>
 
                     <location-editor :locations="v.locations"/>
                 </div>
@@ -35,147 +163,6 @@
 
 </template>
 
-<script>
-import CertInfo from '@/views/domain/cert/CertInfo'
-import IssueCert from '@/views/domain/cert/IssueCert'
-import DirectiveEditor from '@/views/domain/ngx_conf/directive/DirectiveEditor'
-import LocationEditor from '@/views/domain/ngx_conf/LocationEditor'
-
-export default {
-    name: 'NgxConfigEditor',
-    components: {LocationEditor, DirectiveEditor, IssueCert, CertInfo},
-    props: {
-        ngx_config: Object,
-        auto_cert: Boolean,
-        enabled: Boolean
-    },
-    data() {
-        return {
-            current_server_index: 0,
-            update: 0,
-            name: this.$route.params?.name?.toString() ?? '',
-            init_ssl_status: false
-        }
-    },
-    model: {
-        prop: 'auto_cert',
-        event: 'change_auto_cert'
-    },
-    methods: {
-        update_cert_info() {
-            if (this.name && this.$refs['cert-info' + this.current_server_index]) {
-                this.$refs['cert-info' + this.current_server_index].get()
-            }
-        },
-        change_tls(r) {
-            if (r) {
-                // deep copy servers[0] to servers[1]
-                const server = JSON.parse(JSON.stringify(this.ngx_config.servers[0]))
-
-                this.ngx_config.servers.push(server)
-
-                this.current_server_index = 1
-
-                const servers = this.ngx_config.servers
-
-                let i = 0
-                while (i < servers[1].directives.length) {
-                    const v = servers[1].directives[i]
-                    if (v.directive === 'listen') {
-                        servers[1].directives.splice(i, 1)
-                    } else {
-                        i++
-                    }
-                }
-
-                servers[1].directives.splice(0, 0, {
-                    directive: 'listen',
-                    params: '443 ssl http2'
-                }, {
-                    directive: 'listen',
-                    params: '[::]:443 ssl http2'
-                })
-
-                const directivesMap = this.directivesMap
-
-                const server_name = directivesMap['server_name'][0]
-
-                if (!directivesMap['ssl_certificate']) {
-                    servers[1].directives.splice(server_name.idx + 1, 0, {
-                        directive: 'ssl_certificate',
-                        params: ''
-                    })
-                }
-
-                setTimeout(() => {
-                    if (!directivesMap['ssl_certificate_key']) {
-                        servers[1].directives.splice(server_name.idx + 2, 0, {
-                            directive: 'ssl_certificate_key',
-                            params: ''
-                        })
-                    }
-                }, 100)
-
-            } else {
-                // remove servers[1]
-                this.current_server_index = 0
-                if (this.ngx_config.servers.length === 2) {
-                    this.ngx_config.servers.splice(1, 1)
-                }
-            }
-        },
-    },
-    computed: {
-        directivesMap: {
-            get() {
-                const map = {}
-
-                this.current_server_directives.forEach((v, k) => {
-                    v.idx = k
-                    if (map[v.directive]) {
-                        map[v.directive].push(v)
-                    } else {
-                        map[v.directive] = [v]
-                    }
-                })
-
-                return map
-            }
-        },
-        current_server_directives: {
-            get() {
-                return this.ngx_config.servers[this.current_server_index].directives
-            }
-        },
-        support_ssl() {
-            const servers = this.ngx_config.servers
-            for (const server_key in servers) {
-                for (const k in servers[server_key].directives) {
-                    const v = servers[server_key].directives[k]
-                    if (v.directive === 'listen' && v.params.indexOf('ssl') > 0) {
-                        return true
-                    }
-                }
-            }
-            return false
-        },
-        current_support_ssl: {
-            get() {
-                if (this.directivesMap.listen) {
-                    for (const v of this.directivesMap.listen) {
-                        if (v?.params.indexOf('ssl') > 0) {
-                            return true
-                        }
-                    }
-                }
-
-                return false
-            }
-        },
-    }
-}
-</script>
-
 <style scoped>
 
 </style>

+ 63 - 60
frontend-next/src/views/domain/ngx_conf/directive/DirectiveAdd.vue

@@ -1,22 +1,69 @@
+<script setup lang="ts">
+import {If} from '@/views/domain/ngx_conf'
+import CodeEditor from '@/components/CodeEditor'
+import {reactive, ref} from 'vue'
+import {useGettext} from 'vue3-gettext'
+import {CloseOutlined} from '@ant-design/icons-vue'
+
+const {$gettext} = useGettext()
+
+const emit = defineEmits(['save'])
+
+const {ngx_directives, idx} = defineProps(['ngx_directives', 'idx'])
+
+let directive = reactive({directive: '', params: ''})
+const adding = ref(false)
+const mode = ref('default')
+
+
+function add() {
+    adding.value = true
+    directive = reactive({directive: '', params: ''})
+}
+
+function save() {
+    adding.value = false
+    if (mode.value === If) {
+        directive.directive = If
+    }
+
+    if (idx) {
+        ngx_directives.splice(idx + 1, 0, directive)
+    } else {
+        ngx_directives.push(directive)
+    }
+
+    emit('save', idx)
+}
+</script>
+
 <template>
     <div>
         <div class="add-directive-temp" v-if="adding">
-            <a-select v-model="mode" default-value="default" style="min-width: 150px">
-                <a-select-option value="default">
-                    {{ $gettext('Single Directive') }}
-                </a-select-option>
-                <a-select-option value="if">
-                    if
-                </a-select-option>
-            </a-select>
-            <vue-itextarea v-if="mode===If" :default-text-height="100" v-model="directive.params"/>
-            <a-input-group compact v-else>
-                <a-input style="width: 30%" :placeholder="$gettext('Directive')" v-model="directive.directive"/>
-                <a-input style="width: 70%" :placeholder="$gettext('Params')" v-model="directive.params">
-                    <a-icon slot="suffix" type="close" style="color: rgba(0,0,0,.45);font-size: 10px;"
-                            @click="adding=false"/>
-                </a-input>
-            </a-input-group>
+            <a-form-item>
+                <a-select v-model:value="mode" default-value="default" style="width: 150px">
+                    <a-select-option value="default">
+                        {{ $gettext('Single Directive') }}
+                    </a-select-option>
+                    <a-select-option value="if">
+                        if
+                    </a-select-option>
+                </a-select>
+            </a-form-item>
+            <a-form-item>
+                <code-editor v-if="mode===If" default-height="100px" v-model:content="directive.params"/>
+
+                <a-input-group compact v-else>
+
+                    <a-input style="width: 30%" :placeholder="$gettext('Directive')" v-model="directive.directive"/>
+
+                    <a-input style="width: 70%" :placeholder="$gettext('Params')" v-model="directive.params">
+                        <template #suffix>
+                            <CloseOutlined @click="adding=false" style="color: rgba(0,0,0,.45);font-size: 10px;"/>
+                        </template>
+                    </a-input>
+                </a-input-group>
+            </a-form-item>
         </div>
         <a-button block v-if="!adding" @click="add">{{ $gettext('Add Directive Below') }}</a-button>
         <a-button type="primary" v-else block @click="save"
@@ -25,50 +72,6 @@
     </div>
 </template>
 
-<script>
-import {If} from '@/views/domain/ngx_conf/ngx_constant'
-import VueItextarea from '@/components/VueItextarea/VueItextarea'
-
-export default {
-    name: 'DirectiveAdd',
-    components: {
-        VueItextarea
-    },
-    props: {
-        ngx_directives: Array,
-        idx: Number,
-    },
-    data() {
-        return {
-            adding: false,
-            directive: {},
-            mode: 'default',
-            If
-        }
-    },
-    methods: {
-        add() {
-            this.adding = true
-            this.directive = {}
-        },
-        save() {
-            this.adding = false
-            if (this.mode === If) {
-                this.directive.directive = If
-            }
-
-            if (this.idx) {
-                this.ngx_directives.splice(this.idx + 1, 0, this.directive)
-            } else {
-                this.ngx_directives.push(this.directive)
-            }
-
-            this.$emit('save', this.idx)
-        }
-    }
-}
-</script>
-
 <style lang="less" scoped>
 
 </style>

+ 66 - 64
frontend-next/src/views/domain/ngx_conf/directive/DirectiveEditor.vue

@@ -1,75 +1,77 @@
+<script setup lang="ts">
+import CodeEditor from '@/components/CodeEditor'
+import {If} from '@/views/domain/ngx_conf'
+import DirectiveAdd from '@/views/domain/ngx_conf/directive/DirectiveAdd'
+import {useGettext} from 'vue3-gettext'
+import {reactive, ref} from 'vue'
+import {CloseOutlined} from '@ant-design/icons-vue'
+
+const {$gettext} = useGettext()
+
+const {ngx_directives} = defineProps<{
+    ngx_directives: any[]
+}>()
+
+const adding = ref(false)
+
+let directive = reactive({})
+
+const current_idx = ref(-1)
+
+function add() {
+    adding.value = true
+    directive = reactive({})
+}
+
+function save() {
+    adding.value = false
+    ngx_directives.push(directive)
+}
+
+function remove(index: number) {
+    ngx_directives.splice(index, 1)
+}
+
+function onSave(idx: number) {
+    setTimeout(() => {
+        current_idx.value = idx + 1
+    }, 50)
+}
+</script>
+
 <template>
-    <a-form-item :label="$gettext('Directives')">
-        <div v-for="(directive,k) in ngx_directives" :key="k" @click="current_idx=k">
-            <vue-itextarea v-if="directive.directive === If" v-model="directive.params" :default-text-height="100"/>
-            <a-input :addon-before="directive.directive" v-model="directive.params" @click="current_idx=k" v-else>
-                <a-popconfirm slot="suffix" @confirm="remove(k)"
+    <h2>{{ $gettext('Directives') }}</h2>
+
+    <a-form-item v-for="(directive,index) in ngx_directives" @click="current_idx=index">
+        <code-editor v-if="directive.directive === If" v-model:content="directive.params"
+                     defaultHeight="100px"/>
+        <a-input :addon-before="directive.directive" v-model:value="directive.params" @click="current_idx=k"
+                 v-else>
+            <template #suffix>
+                <a-popconfirm @confirm="remove(index)"
                               :title="$gettext('Are you sure you want to remove this directive?')"
                               :ok-text="$gettext('Yes')"
                               :cancel-text="$gettext('No')">
-                    <a-icon type="close"
-                            style="color: rgba(0,0,0,.45);font-size: 10px;"
-                    />
+                    <CloseOutlined style="color: rgba(0,0,0,.45);font-size: 10px;"/>
                 </a-popconfirm>
-            </a-input>
-            <transition name="slide">
-                <div v-if="current_idx===k" class="extra">
-                    <div class="extra-content">
+            </template>
+        </a-input>
+        <transition name="slide">
+            <div v-if="current_idx===index" class="extra">
+                <div class="extra-content">
+                    <a-form layout="vertical">
                         <a-form-item :label="$gettext('Comments')">
                             <a-textarea v-model="directive.comments"/>
                         </a-form-item>
-                        <directive-add :ngx_directives="ngx_directives" :idx="k" @save="onSave(k)"/>
-                    </div>
+                    </a-form>
+                    <directive-add :ngx_directives="ngx_directives" :idx="index" @save="onSave(index)"/>
                 </div>
-            </transition>
-        </div>
-        <directive-add :ngx_directives="ngx_directives"/>
+            </div>
+        </transition>
     </a-form-item>
-</template>
 
-<script>
-import VueItextarea from '@/components/VueItextarea/VueItextarea'
-import {If} from '../ngx_constant'
-import DirectiveAdd from '@/views/domain/ngx_conf/directive/DirectiveAdd'
-
-export default {
-    name: 'DirectiveEditor',
-    props: {
-        ngx_directives: Array
-    },
-    components: {
-        DirectiveAdd,
-        VueItextarea
-    },
-    data() {
-        return {
-            adding: false,
-            directive: {},
-            If,
-            current_idx: -1,
-        }
-    },
-    methods: {
-        add() {
-            this.adding = true
-            this.directive = {}
-        },
-        save() {
-            this.adding = false
-            this.ngx_directives.push(this.directive)
-        },
-        remove(index) {
-            this.ngx_directives.splice(index, 1)
-        },
-        onSave(idx) {
-            const that = this
-            setTimeout(() => {
-                that.current_idx = idx + 1
-            }, 50)
-        }
-    }
-}
-</script>
+    <directive-add :ngx_directives="ngx_directives"/>
+</template>
 
 <style lang="less" scoped>
 .extra {
@@ -79,14 +81,14 @@ export default {
 }
 
 .slide-enter-active, .slide-leave-active {
-    transition: max-height .5s ease;
+    transition: max-height .3s ease;
 }
 
-.slide-enter, .slide-leave-to {
+.slide-enter-from, .slide-leave-to {
     max-height: 0;
 }
 
-.slide-enter-to, .slide-leave {
+.slide-enter-to, .slide-leave-from {
     max-height: 600px;
 }
 </style>

+ 0 - 0
frontend-next/src/views/domain/ngx_conf/ngx_constant.js → frontend-next/src/views/domain/ngx_conf/index.ts


+ 1 - 1
frontend-next/version.json

@@ -1 +1 @@
-{"version":"1.5.0","build_id":6,"total_build":76}
+{"version":"1.5.0","build_id":8,"total_build":78}

+ 3 - 0
frontend-next/vite.config.ts

@@ -72,4 +72,7 @@ export default defineConfig({
             },
         },
     },
+    build: {
+        chunkSizeWarningLimit: 600
+    }
 })

+ 124 - 118
server/api/cert.go

@@ -1,128 +1,134 @@
 package api
 
 import (
-    "github.com/0xJacky/Nginx-UI/server/tool"
-    "github.com/0xJacky/Nginx-UI/server/tool/nginx"
-    "github.com/gin-gonic/gin"
-    "github.com/gorilla/websocket"
-    "log"
-    "net/http"
-    "os"
+	"github.com/0xJacky/Nginx-UI/server/tool"
+	"github.com/0xJacky/Nginx-UI/server/tool/nginx"
+	"github.com/gin-gonic/gin"
+	"github.com/gorilla/websocket"
+	"log"
+	"net/http"
+	"os"
 )
 
 func CertInfo(c *gin.Context) {
-    domain := c.Param("domain")
-
-    key, err := tool.GetCertInfo(domain)
-
-    c.JSON(http.StatusOK, gin.H{
-        "error":        err,
-        "subject_name": key.Subject.CommonName,
-        "issuer_name":  key.Issuer.CommonName,
-        "not_after":    key.NotAfter,
-        "not_before":   key.NotBefore,
-    })
+	domain := c.Param("domain")
+
+	key, err := tool.GetCertInfo(domain)
+
+	if err != nil {
+		c.JSON(http.StatusOK, gin.H{
+			"error": err,
+		})
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"subject_name": key.Subject.CommonName,
+		"issuer_name":  key.Issuer.CommonName,
+		"not_after":    key.NotAfter,
+		"not_before":   key.NotBefore,
+	})
 }
 
 func IssueCert(c *gin.Context) {
-    domain := c.Param("domain")
-    var upGrader = websocket.Upgrader{
-        CheckOrigin: func(r *http.Request) bool {
-            return true
-        },
-    }
-
-    // upgrade http to websocket
-    ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
-    if err != nil {
-        log.Println(err)
-        return
-    }
-
-    defer func(ws *websocket.Conn) {
-        err := ws.Close()
-        if err != nil {
-            log.Println("defer websocket close err", err)
-        }
-    }(ws)
-
-    // read
-    mt, message, err := ws.ReadMessage()
-    if err != nil {
-        log.Println(err)
-        return
-    }
-
-    if mt == websocket.TextMessage && string(message) == "go" {
-
-        err = tool.IssueCert(domain)
-
-        if err != nil {
-
-            log.Println(err)
-
-            err = ws.WriteJSON(gin.H{
-                "status":  "error",
-                "message": err.Error(),
-            })
-
-            if err != nil {
-                log.Println(err)
-                return
-            }
-
-            return
-        }
-
-        sslCertificatePath := nginx.GetNginxConfPath("ssl/" + domain + "/fullchain.cer")
-        _, err = os.Stat(sslCertificatePath)
-
-        if err != nil {
-            log.Println(err)
-            return
-        }
-
-        log.Println("[found]", "fullchain.cer")
-
-        err = ws.WriteJSON(gin.H{
-            "status":  "success",
-            "message": "[found] fullchain.cer",
-        })
-
-        if err != nil {
-            log.Println(err)
-            return
-        }
-
-        sslCertificateKeyPath := nginx.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key")
-        _, err = os.Stat(sslCertificateKeyPath)
-
-        if err != nil {
-            log.Println(err)
-            return
-        }
-
-        log.Println("[found]", "cert key")
-        err = ws.WriteJSON(gin.H{
-            "status":  "success",
-            "message": "[found] Certificate Key",
-        })
-
-        if err != nil {
-            log.Println(err)
-            return
-        }
-
-        err = ws.WriteJSON(gin.H{
-            "status":              "success",
-            "message":             "Issued certificate successfully",
-            "ssl_certificate":     sslCertificatePath,
-            "ssl_certificate_key": sslCertificateKeyPath,
-        })
-
-        if err != nil {
-            log.Println(err)
-            return
-        }
-    }
+	domain := c.Param("domain")
+	var upGrader = websocket.Upgrader{
+		CheckOrigin: func(r *http.Request) bool {
+			return true
+		},
+	}
+
+	// upgrade http to websocket
+	ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
+	if err != nil {
+		log.Println(err)
+		return
+	}
+
+	defer func(ws *websocket.Conn) {
+		err := ws.Close()
+		if err != nil {
+			log.Println("defer websocket close err", err)
+		}
+	}(ws)
+
+	// read
+	mt, message, err := ws.ReadMessage()
+	if err != nil {
+		log.Println(err)
+		return
+	}
+
+	if mt == websocket.TextMessage && string(message) == "go" {
+
+		err = tool.IssueCert(domain)
+
+		if err != nil {
+
+			log.Println(err)
+
+			err = ws.WriteJSON(gin.H{
+				"status":  "error",
+				"message": err.Error(),
+			})
+
+			if err != nil {
+				log.Println(err)
+				return
+			}
+
+			return
+		}
+
+		sslCertificatePath := nginx.GetNginxConfPath("ssl/" + domain + "/fullchain.cer")
+		_, err = os.Stat(sslCertificatePath)
+
+		if err != nil {
+			log.Println(err)
+			return
+		}
+
+		log.Println("[found]", "fullchain.cer")
+
+		err = ws.WriteJSON(gin.H{
+			"status":  "success",
+			"message": "[found] fullchain.cer",
+		})
+
+		if err != nil {
+			log.Println(err)
+			return
+		}
+
+		sslCertificateKeyPath := nginx.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key")
+		_, err = os.Stat(sslCertificateKeyPath)
+
+		if err != nil {
+			log.Println(err)
+			return
+		}
+
+		log.Println("[found]", "cert key")
+		err = ws.WriteJSON(gin.H{
+			"status":  "success",
+			"message": "[found] Certificate Key",
+		})
+
+		if err != nil {
+			log.Println(err)
+			return
+		}
+
+		err = ws.WriteJSON(gin.H{
+			"status":              "success",
+			"message":             "Issued certificate successfully",
+			"ssl_certificate":     sslCertificatePath,
+			"ssl_certificate_key": sslCertificateKeyPath,
+		})
+
+		if err != nil {
+			log.Println(err)
+			return
+		}
+	}
 }