|
42 | 42 | <span v-if="loadDNSRecordsLoading" class="loading loading-spinner"></span>
|
43 | 43 | Load Selected Zone's DNS Records
|
44 | 44 | </button>
|
45 |
| - <button class="btn btn-sm" :disabled="!hasSelectedZone || loadPageRulesLoading" |
46 |
| - @click="loadPageRules"> |
47 |
| - <span v-if="loadPageRulesLoading" class="loading loading-spinner"></span> |
48 |
| - Load Selected Zone's Page Rules |
49 |
| - </button> |
50 | 45 | <button class="btn btn-sm" :disabled="!hasSelectedRecord || deleteDNSRecordsLoading"
|
51 | 46 | @click="deleteDNSRecords">
|
52 | 47 | <span v-if="deleteDNSRecordsLoading" class="loading loading-spinner"></span>
|
53 | 48 | Delete Selected DNS Records
|
54 | 49 | </button>
|
| 50 | + <button class="btn btn-sm" :disabled="!hasSelectedZone || loadPageRulesLoading" |
| 51 | + @click="loadPageRules"> |
| 52 | + <span v-if="loadPageRulesLoading" class="loading loading-spinner"></span> |
| 53 | + Load Selected Zone's Page Rules |
| 54 | + </button> |
55 | 55 | <button class="btn btn-sm" :disabled="!hasSelectedPageRule || deletePageRulesLoading"
|
56 | 56 | @click="deletePageRules">
|
57 | 57 | <span v-if="deletePageRulesLoading" class="loading loading-spinner"></span>
|
58 | 58 | Delete Selected Page Rules
|
59 | 59 | </button>
|
| 60 | + <button class="btn btn-sm" :disabled="!hasSelectedZone || loadRulesetsLoading" |
| 61 | + @click="loadRulesets"> |
| 62 | + <span v-if="loadRulesetsLoading" class="loading loading-spinner"></span> |
| 63 | + Load Selected Zone's Rulesets |
| 64 | + </button> |
| 65 | + <button class="btn btn-sm" :disabled="!hasSelectedRulesets || deleteRulesetsLoading" |
| 66 | + @click="deleteRulesets"> |
| 67 | + <span v-if="deleteRulesetsLoading" class="loading loading-spinner"></span> |
| 68 | + Delete Selected Ruleset |
| 69 | + </button> |
| 70 | + <p class="mt-2 text-sm"> |
| 71 | + Permissions Required: Zone_DNS, Account_AccountRulesets, |
| 72 | + Zone_DynamicRedirect, Zone_PageRules |
| 73 | + </p> |
60 | 74 | </div>
|
61 | 75 | </div>
|
62 | 76 |
|
|
67 | 81 | <div class="label">
|
68 | 82 | <span class="label-text">Name</span>
|
69 | 83 | </div>
|
70 |
| - <input v-model="newDNSRecord.name" type="text" placeholder="@.example.com" |
| 84 | + <input v-model="newDNSRecord.name" type="text" placeholder="@" |
71 | 85 | class="input input-bordered" />
|
72 | 86 | </label>
|
73 | 87 | <label class="form-control">
|
|
106 | 120 | <span v-if="batchCreateDNSRecordLoading" class="loading loading-spinner"></span>
|
107 | 121 | Batch Create
|
108 | 122 | </button>
|
109 |
| - <p class="mt-2"> |
| 123 | + <p class="mt-2 text-sm"> |
110 | 124 | <i>* <code>#DOMAIN#</code> in content or name will be replaced with the domain name. eg.
|
111 | 125 | <code>example.com</code></i><br>
|
112 | 126 | <i>* <code>#DOMAIN.SUFFIX#</code> in content or name will be replaced with the domain
|
|
124 | 138 | <div class="label">
|
125 | 139 | <span class="label-text">Actions</span>
|
126 | 140 | </div>
|
127 |
| - <textarea v-model="newPageRule.actions" type="text"class="textarea textarea-bordered"></textarea> |
| 141 | + <textarea v-model="newPageRule.actions" type="text" |
| 142 | + class="textarea textarea-bordered"></textarea> |
128 | 143 | </label>
|
129 | 144 | <label class="form-control">
|
130 | 145 | <div class="label">
|
131 | 146 | <span class="label-text">Targets</span>
|
132 | 147 | </div>
|
133 |
| - <textarea v-model="newPageRule.targets" type="text"class="textarea textarea-bordered"></textarea> |
| 148 | + <textarea v-model="newPageRule.targets" type="text" |
| 149 | + class="textarea textarea-bordered"></textarea> |
134 | 150 | </label>
|
135 | 151 | <label class="form-control">
|
136 | 152 | <div class="label">
|
|
143 | 159 | <span v-if="batchCreatePageRuleLoading" class="loading loading-spinner"></span>
|
144 | 160 | Batch Create
|
145 | 161 | </button>
|
146 |
| - <p class="mt-2"> |
| 162 | + <p class="mt-2 text-sm"> |
147 | 163 | <i>* <code>#DOMAIN#</code> in content or name will be replaced with the domain name. eg.
|
148 | 164 | <code>example.com</code></i><br>
|
149 | 165 | <i>* <code>#DOMAIN.SUFFIX#</code> in content or name will be replaced with the domain
|
|
153 | 169 | without the last part. eg. <code>example</code></i>
|
154 | 170 | </p>
|
155 | 171 | </div>
|
| 172 | + |
| 173 | + <input type="radio" name="my_tabs_2" role="tab" class="tab min-w-max" |
| 174 | + aria-label="Batch Create Ruleset" /> |
| 175 | + <div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6"> |
| 176 | + <textarea v-model="newRuleset" type="text" |
| 177 | + class="textarea textarea-bordered block w-full"></textarea> |
| 178 | + <button class="btn btn-sm mt-2" :disabled="!hasSelectedZone || batchCreateRulesetLoading" |
| 179 | + @click="batchCreateRuleset"> |
| 180 | + <span v-if="batchCreateRulesetLoading" class="loading loading-spinner"></span> |
| 181 | + Batch Create |
| 182 | + </button> |
| 183 | + <p class="mt-2 text-sm"> |
| 184 | + <i>* <code>#DOMAIN#</code> in content will be replaced with the domain name. eg. |
| 185 | + <code>example.com</code></i><br> |
| 186 | + <i>* <code>#DOMAIN.SUFFIX#</code> in content will be replaced with the domain |
| 187 | + name without the first part. eg. <code>com</code></i><br> |
| 188 | + <i>* <code>#DOMAIN.PREFIX#</code> in content will be replaced with the domain |
| 189 | + name |
| 190 | + without the last part. eg. <code>example</code></i> |
| 191 | + </p> |
| 192 | + </div> |
156 | 193 | </div>
|
157 | 194 | </div>
|
158 | 195 | <div class="overflow-x-scroll mt-2">
|
|
166 | 203 | @click="toggleSelectAllRecords" />
|
167 | 204 | <input type="checkbox" class="ml-2 checkbox checkbox-xs" :checked="selectedAllPageRules"
|
168 | 205 | @click="toggleSelectAllPageRules" />
|
| 206 | + <input type="checkbox" class="ml-2 checkbox checkbox-xs" :checked="selectedAllRulesets" |
| 207 | + @click="toggleSelectAllRulesets" /> |
169 | 208 | </th>
|
170 | 209 | <th>Name</th>
|
171 | 210 | <th>Status</th>
|
|
221 | 260 | <td></td>
|
222 | 261 | <td></td>
|
223 | 262 | </tr>
|
| 263 | + <tr class="bg-primary-200" v-for="(ruleset,sIndex) in rulesets[zone.id]" :key="ruleset.id"> |
| 264 | + <th> |
| 265 | + <input type="checkbox" class="ml-[4.5rem] checkbox checkbox-xs" |
| 266 | + :checked="ruleset.selected" @click="toggleSelectRuleset(zone.id,sIndex)" /> |
| 267 | + </th> |
| 268 | + <td> |
| 269 | + <div class="badge badge-sm badge-info badge-outline">Ruleset</div> |
| 270 | + </td> |
| 271 | + <td>{{ ruleset.kind }}</td> |
| 272 | + <td> |
| 273 | + </td> |
| 274 | + <td class="max-w-40 text-ellipsis overflow-hidden">{{ ruleset.phase }}</td> |
| 275 | + <td></td> |
| 276 | + <td></td> |
| 277 | + </tr> |
224 | 278 | </template>
|
225 | 279 | </tbody>
|
226 | 280 | </table>
|
|
234 | 288 | const selectedAll = ref(false)
|
235 | 289 | const selectedAllRecords = ref(false)
|
236 | 290 | const selectedAllPageRules = ref(false)
|
| 291 | + const selectedAllRulesets = ref(false) |
237 | 292 | const deleteDNSRecordsLoading = ref(false)
|
238 | 293 | const deletePageRulesLoading = ref(false)
|
| 294 | + const deleteRulesetsLoading = ref(false) |
239 | 295 | const loadDNSRecordsLoading = ref(false)
|
240 | 296 | const loadPageRulesLoading = ref(false)
|
| 297 | + const loadRulesetsLoading = ref(false) |
241 | 298 | const zones = ref(null)
|
242 | 299 | const dnsRecords = ref({})
|
243 | 300 | const pageRules = ref({})
|
| 301 | + const rulesets = ref({}) |
244 | 302 | const apiToken = ref(null)
|
245 | 303 | const newDNSRecord = ref({
|
246 | 304 | type: 'A',
|
247 | 305 | ttl: 1,
|
248 | 306 | })
|
249 | 307 | const newPageRule = ref({
|
250 |
| - actions: '[{"id":"forwarding_url","value":{"status_code":302,"url":"https://bigtoyscompany.com/assets/5.nu?utm_source=5.nu"}}]', |
251 |
| - targets: '[{"target":"url","constraint":{"operator":"matches","value":"5.nu/*"}}]', |
| 308 | + actions: '[{"id":"forwarding_url","value":{"status_code":302,"url":"https://example.com/assets/#DOMAIN#?utm_source#DOMAIN#"}}]', |
| 309 | + targets: '[{"target":"url","constraint":{"operator":"matches","value":"#DOMAIN#/*"}}]', |
252 | 310 | priority: 1,
|
253 | 311 | })
|
| 312 | + const newRuleset = ref('{"name":"Domain Portfolio","kind":"zone","phase":"http_request_dynamic_redirect","description":"domain_portfolio","rules":[{"action":"redirect","action_parameters":{"from_value":{"status_code":302,"target_url":{"value":"https://example.com/assets/#DOMAIN#?utm_source=#DOMAIN#"},"preserve_query_string":false}},"description":"domain_portfolio","expression":"true","enabled":true}]}') |
254 | 313 | const batchCreateDNSRecordLoading = ref(false)
|
255 | 314 | const batchCreatePageRuleLoading = ref(false)
|
| 315 | + const batchCreateRulesetLoading = ref(false) |
256 | 316 |
|
257 | 317 | const hasSelectedZone = computed(() => zones.value && zones.value.some(zone => zone.selected))
|
258 | 318 | const hasSelectedRecord = computed(() => Object.values(dnsRecords.value).some(records => records.some(record => record.selected)))
|
259 | 319 | const hasSelectedPageRule = computed(() => Object.values(pageRules.value).some(records => records.some(record => record.selected)))
|
| 320 | + const hasSelectedRulesets = computed(() => Object.values(rulesets.value).some(records => records.some(record => record.selected))) |
260 | 321 |
|
261 | 322 | fetch('/state').then(async response => {
|
262 | 323 | if (response.ok) {
|
|
265 | 326 | zones.value = data.zones
|
266 | 327 | dnsRecords.value = data.dnsRecords
|
267 | 328 | pageRules.value = data.pageRules
|
| 329 | + rulesets.value = data.rulesets |
268 | 330 | }
|
269 | 331 | })
|
270 | 332 |
|
|
301 | 363 | Object.values(pageRules.value).forEach(records => records.forEach(record => record.selected = selectedAllPageRules.value))
|
302 | 364 | }
|
303 | 365 |
|
| 366 | + const toggleSelectAllRulesets = () => { |
| 367 | + selectedAllRulesets.value = !selectedAllRulesets.value |
| 368 | + Object.values(rulesets.value).forEach(records => records.forEach(record => record.selected = selectedAllRulesets.value)) |
| 369 | + } |
| 370 | + |
304 | 371 | const toggleSelect = (index) => {
|
305 | 372 | zones.value[index].selected = !zones.value[index].selected
|
306 | 373 | selectedAll.value = zones.value.every(zone => zone.selected)
|
|
316 | 383 | selectedAllPageRules.value = Object.values(pageRules.value).every(records => records.every(record => record.selected))
|
317 | 384 | }
|
318 | 385 |
|
| 386 | + const toggleSelectRuleset = (zoneId, rIndex) => { |
| 387 | + rulesets.value[zoneId][rIndex].selected = !rulesets.value[zoneId][rIndex].selected |
| 388 | + selectedAllRulesets.value = Object.values(rulesets.value).every(records => records.every(record => record.selected)) |
| 389 | + } |
| 390 | + |
319 | 391 | const loadPageRules = (zone) => {
|
320 | 392 | loadPageRulesLoading.value = true
|
321 | 393 | fetch(`/load-zone-page-rules`, {
|
|
348 | 420 | })
|
349 | 421 | }
|
350 | 422 |
|
| 423 | + const loadRulesets = (zone) => { |
| 424 | + loadRulesetsLoading.value = true |
| 425 | + fetch(`/load-zone-rulesets`, { |
| 426 | + method: 'POST', |
| 427 | + body: JSON.stringify(zones.value.filter(zone => zone.selected).map(zone => zone.id)), |
| 428 | + }).then(async response => { |
| 429 | + if (!response.ok) { |
| 430 | + alert(await response.text()) |
| 431 | + return |
| 432 | + } |
| 433 | + rulesets.value = await response.json() |
| 434 | + }).finally(() => { |
| 435 | + loadRulesetsLoading.value = false |
| 436 | + }) |
| 437 | + } |
| 438 | + |
351 | 439 | const deleteDNSRecords = () => {
|
352 | 440 | deleteDNSRecordsLoading.value = true
|
353 | 441 | const data = {}
|
|
398 | 486 | })
|
399 | 487 | }
|
400 | 488 |
|
| 489 | + const deleteRulesets = () => { |
| 490 | + deleteRulesetsLoading.value = true |
| 491 | + const data = {} |
| 492 | + Object.entries(rulesets.value).forEach(([zoneId, records]) => { |
| 493 | + data[zoneId] = records.filter(record => record.selected).map(record => record.id) |
| 494 | + }) |
| 495 | + Object.entries(data).forEach(([zoneId, records]) => { |
| 496 | + if (records.length === 0) { |
| 497 | + delete data[zoneId] |
| 498 | + } |
| 499 | + }) |
| 500 | + fetch('/delete-rulesets', { |
| 501 | + method: 'POST', |
| 502 | + body: JSON.stringify(data), |
| 503 | + }).then(async response => { |
| 504 | + if (!response.ok) { |
| 505 | + alert(await response.text()) |
| 506 | + return |
| 507 | + } |
| 508 | + alert('Purge Rulesets success, please reload Rulesets to see the changes.') |
| 509 | + }).finally(() => { |
| 510 | + deleteRulesetsLoading.value = false |
| 511 | + }) |
| 512 | + } |
| 513 | + |
401 | 514 | const batchCreateDNSRecord = () => {
|
402 | 515 | batchCreateDNSRecordLoading.value = true
|
403 | 516 | fetch('/batch-create-dns-record', {
|
|
440 | 553 | })
|
441 | 554 | }
|
442 | 555 |
|
| 556 | + const batchCreateRuleset = () => { |
| 557 | + batchCreateRulesetLoading.value = true |
| 558 | + fetch('/batch-create-ruleset', { |
| 559 | + method: 'POST', |
| 560 | + body: JSON.stringify({ |
| 561 | + zones: zones.value.filter(zone => zone.selected).map(zone => zone.id), |
| 562 | + ruleset: newRuleset.value |
| 563 | + }), |
| 564 | + }).then(async response => { |
| 565 | + if (!response.ok) { |
| 566 | + alert(await response.text()) |
| 567 | + return |
| 568 | + } |
| 569 | + alert('Create Rulesets success, please reload Rulesets to see the changes.') |
| 570 | + }).finally(() => { |
| 571 | + batchCreateRulesetLoading.value = false |
| 572 | + }) |
| 573 | + } |
| 574 | + |
443 | 575 | return {
|
444 | 576 | checkApiToken,
|
445 | 577 | zones,
|
|
472 | 604 | newPageRule,
|
473 | 605 | batchCreatePageRule,
|
474 | 606 | batchCreatePageRuleLoading,
|
| 607 | + rulesets, |
| 608 | + loadRulesets, |
| 609 | + loadRulesetsLoading, |
| 610 | + selectedAllRulesets, |
| 611 | + toggleSelectAllRulesets, |
| 612 | + toggleSelectRuleset, |
| 613 | + deleteRulesets, |
| 614 | + deleteRulesetsLoading, |
| 615 | + hasSelectedRulesets, |
| 616 | + newRuleset, |
| 617 | + batchCreateRuleset, |
| 618 | + batchCreateRulesetLoading, |
475 | 619 | }
|
476 | 620 | }
|
477 | 621 | }).mount('#app')
|
|
0 commit comments