Create.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <base-dialog @cancel="cancelDialog">
  3. <div slot="header">{{ params.title || $t('network.create_cdn_custom_hostname')}}</div>
  4. <div slot="body">
  5. <a-form-model
  6. :model="form"
  7. :rules="rules"
  8. ref="form"
  9. v-bind="formItemLayout">
  10. <a-form-model-item :label="$t('network.cdn.hostname')" prop="hostname">
  11. <a-input v-model="form.hostname" />
  12. </a-form-model-item>
  13. <a-form-model-item :label="$t('network.cdn.min_tls_version')" prop="min_tls_version">
  14. <a-select v-model="form.min_tls_version">
  15. <a-select-option v-for="item in MIN_TLS_VERSIONS" :key="item.key" :value="item.key">
  16. {{ item.label }}
  17. </a-select-option>
  18. </a-select>
  19. </a-form-model-item>
  20. <a-form-model-item :label="$t('network.cdn.certificate_type')" prop="certificate_type">
  21. <a-select v-model="form.certificate_type">
  22. <a-select-option value="cloudflare">
  23. {{ $t('network.cdn.certificate_type_cloudflare') }}
  24. </a-select-option>
  25. <a-select-option value="custom">
  26. {{ $t('network.cdn.certificate_type_custom') }}
  27. </a-select-option>
  28. </a-select>
  29. </a-form-model-item>
  30. <template v-if="form.certificate_type === 'cloudflare'">
  31. <a-form-model-item :label="$t('network.cdn.certificate_authority')" prop="certificate_authority">
  32. <a-select v-model="form.certificate_authority">
  33. <a-select-option value="google">
  34. {{ $t('network.cdn.certificate_authority.google') }}
  35. </a-select-option>
  36. <a-select-option value="ssl_com">
  37. {{ $t('network.cdn.certificate_authority.ssl_com') }}
  38. </a-select-option>
  39. <a-select-option value="lets_encrypt">
  40. {{ $t('network.cdn.certificate_authority.lets_encrypt') }}
  41. </a-select-option>
  42. </a-select>
  43. </a-form-model-item>
  44. <a-form-model-item :label="$t('network.cdn.ceritificate_verify_method')" prop="method">
  45. <a-select v-model="form.method">
  46. <a-select-option value="http">
  47. {{ $t('network.cdn.certificate_verify_method.http') }}
  48. </a-select-option>
  49. <a-select-option value="txt">
  50. {{ $t('network.cdn.certificate_verify_method.txt') }}
  51. </a-select-option>
  52. </a-select>
  53. </a-form-model-item>
  54. </template>
  55. <template v-else>
  56. <a-form-model-item :label="$t('network.cdn.certificate_type_custom')" prop="custom_certificate">
  57. <a-textarea v-model="form.custom_certificate" :placeholder="$t('common.pem_certificate.placeholder')" :rows="5" />
  58. </a-form-model-item>
  59. <a-form-model-item :label="$t('network.cdn.bundle_method')" prop="bundle_method">
  60. <a-select v-model="form.bundle_method">
  61. <a-select-option value="ubiquitous">
  62. {{ $t('network.cdn.bundle_method.ubiquitous') }}
  63. </a-select-option>
  64. <a-select-option value="optimal">
  65. {{ $t('network.cdn.bundle_method.optimal') }}
  66. </a-select-option>
  67. <a-select-option value="force">
  68. {{ $t('network.cdn.bundle_method.force') }}
  69. </a-select-option>
  70. </a-select>
  71. </a-form-model-item>
  72. <a-form-model-item :label="$t('network.cdn.custom_key')" prop="custom_key">
  73. <a-textarea v-model="form.custom_key" :placeholder="$t('common.pem_private_key.placeholder')" :rows="5" />
  74. </a-form-model-item>
  75. </template>
  76. <a-form-model-item :label="$t('network.cdn.wildcard_enabled')" required>
  77. <a-switch v-model="form.wildcard" :disabled="form.method === 'http'" />
  78. </a-form-model-item>
  79. <a-form-model-item :label="$t('network.cdn.origin_server')" prop="origin_server">
  80. <a-select v-model="form.origin_server">
  81. <a-select-option value="default">
  82. {{ $t('network.cdn.origin_server.default') }}
  83. </a-select-option>
  84. <a-select-option value="custom">
  85. {{ $t('network.cdn.origin_server.custom') }}
  86. </a-select-option>
  87. </a-select>
  88. </a-form-model-item>
  89. <a-form-model-item v-if="form.origin_server === 'custom'" :label="$t('network.cdn.origin_server.custom')" prop="custom_origin_server">
  90. <a-input v-model="form.custom_origin_server" />
  91. </a-form-model-item>
  92. </a-form-model>
  93. </div>
  94. <div slot="footer">
  95. <a-button type="primary" v-if="params.type !== 'edit'" @click="handleConfirm" :loading="loading">{{ $t('dialog.ok') }}</a-button>
  96. <a-button @click="cancelDialog">{{ $t('dialog.cancel') }}</a-button>
  97. </div>
  98. </base-dialog>
  99. </template>
  100. <script>
  101. import DialogMixin from '@/mixins/dialog'
  102. import WindowsMixin from '@/mixins/windows'
  103. import { validateModelForm } from '@/utils/validate'
  104. import { MIN_TLS_VERSIONS } from '../constants'
  105. export default {
  106. name: 'CdnHostnameCreateDialog',
  107. mixins: [DialogMixin, WindowsMixin],
  108. data () {
  109. const data = this.params.data.length ? this.params.data[0] : {}
  110. return {
  111. loading: false,
  112. MIN_TLS_VERSIONS,
  113. formItemLayout: {
  114. wrapperCol: {
  115. span: 20,
  116. },
  117. labelCol: {
  118. span: 4,
  119. },
  120. },
  121. form: {
  122. hostname: data.hostname,
  123. min_tls_version: data.ssl?.settings?.min_tls_version || '1.0',
  124. certificate_type: data.ssl?.type === 'dv' || !this.params.data.length ? 'cloudflare' : 'custom',
  125. certificate_authority: data.ssl?.certificate_authority || 'google',
  126. method: data.ssl?.method || 'txt',
  127. custom_key: data.ssl?.private_key || '', // 私钥
  128. custom_certificate: data.ssl?.custom_certificate || '', // 自定义证书
  129. bundle_method: data.ssl?.bundle_method || 'ubiquitous', // 捆绑方法
  130. origin_server: data.custom_origin_server ? 'custom' : 'default', // 源服务器
  131. custom_origin_server: data.custom_origin_server || '', // 自定义源服务器
  132. wildcard: data.ssl?.wildcard || false,
  133. },
  134. rules: {
  135. hostname: [
  136. {
  137. required: true,
  138. validator: this.validateHostname,
  139. },
  140. ],
  141. min_tls_version: [
  142. { required: true },
  143. ],
  144. certificate_type: [
  145. { required: true },
  146. ],
  147. certificate_authority: [
  148. { required: true },
  149. ],
  150. method: [
  151. { required: true },
  152. ],
  153. custom_certificate: [
  154. {
  155. required: true,
  156. validator: this.$validate('pem_certificate'),
  157. },
  158. ],
  159. bundle_method: [
  160. { required: true },
  161. ],
  162. custom_key: [
  163. { required: true, validator: this.$validate('pem_private_key') },
  164. ],
  165. origin_server: [
  166. { required: true },
  167. ],
  168. custom_origin_server: [
  169. { required: true, validator: this.validateHostname },
  170. ],
  171. },
  172. }
  173. },
  174. computed: {
  175. },
  176. watch: {
  177. 'form.method': {
  178. handler (val) {
  179. if (val === 'http') {
  180. this.form.wildcard = false
  181. }
  182. },
  183. },
  184. },
  185. methods: {
  186. validateHostname (rule, value, callback) {
  187. // 基本格式验证
  188. const basicPattern = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/
  189. if (!basicPattern.test(value)) {
  190. callback(new Error(this.$t('network.cdn.hostname.check.format')))
  191. return
  192. }
  193. // 长度验证(小于256字符)
  194. if (value.length >= 256) {
  195. callback(new Error(this.$t('network.cdn.hostname.check.length')))
  196. return
  197. }
  198. // 不能是IP地址
  199. const ipPattern = /^(\d{1,3}\.){3}\d{1,3}$/
  200. if (ipPattern.test(value)) {
  201. callback(new Error(this.$t('network.cdn.hostname.check.ip')))
  202. return
  203. }
  204. // 不能包含特殊字符
  205. const specialCharPattern = /[_~`!@#$%^*()=+{}[\]|\\;:'",<>/?]/
  206. if (specialCharPattern.test(value)) {
  207. callback(new Error(this.$t('network.cdn.hostname.check.special')))
  208. return
  209. }
  210. // 不能以连字符开头或结尾
  211. if (value.startsWith('-') || value.endsWith('-')) {
  212. callback(new Error(this.$t('network.cdn.hostname.check.special_line')))
  213. return
  214. }
  215. // 不能以禁止的域名结尾
  216. const prohibitedPattern = /\.(example\.com|example\.net|example\.org)$/i
  217. if (prohibitedPattern.test(value)) {
  218. callback(new Error(this.$t('network.cdn.hostname.check.prohibited')))
  219. return
  220. }
  221. callback()
  222. },
  223. doCreate (data) {
  224. return new this.$Manager(`cdn_domains/${this.params.cdnDomainId}/add-custom-hostname`).create({ data })
  225. },
  226. genParams () {
  227. const ret = {
  228. hostname: this.form.hostname,
  229. ssl: {
  230. settings: { min_tls_version: this.form.min_tls_version },
  231. wildcard: this.form.wildcard,
  232. },
  233. }
  234. if (this.form.certificate_type === 'cloudflare') {
  235. ret.ssl.certificate_authority = this.form.certificate_authority
  236. ret.ssl.method = this.form.method
  237. ret.ssl.type = 'dv'
  238. } else {
  239. ret.ssl.custom_certificate = this.form.custom_certificate
  240. ret.ssl.private_key = this.form.custom_key
  241. ret.ssl.bundle_method = this.form.bundle_method
  242. }
  243. if (this.form.origin_server === 'custom') {
  244. ret.custom_origin_server = this.form.custom_origin_server
  245. }
  246. return ret
  247. },
  248. async handleConfirm () {
  249. this.loading = true
  250. try {
  251. await validateModelForm(this.$refs.form)
  252. const values = this.genParams()
  253. await this.doCreate(values)
  254. this.loading = false
  255. this.params.success()
  256. this.cancelDialog()
  257. } catch (error) {
  258. this.loading = false
  259. }
  260. },
  261. },
  262. }
  263. </script>