|
| 1 | +<svelte:options tag="tf-discourse" /> |
| 2 | + |
| 3 | +<script lang="ts"> |
| 4 | + import type { IFormField, ITab } from "../../types"; |
| 5 | + import { default as Discourse } from "../../types/discourse"; |
| 6 | + import deployDiscourse from "../../utils/deployDiscourse"; |
| 7 | + import type { IProfile } from "../../types/Profile"; |
| 8 | + import rootFs from "../../utils/rootFs"; |
| 9 | + // Components |
| 10 | + import SelectProfile from "../../components/SelectProfile.svelte"; |
| 11 | + import Input from "../../components/Input.svelte"; |
| 12 | + import Tabs from "../../components/Tabs.svelte"; |
| 13 | + import DeployBtn from "../../components/DeployBtn.svelte"; |
| 14 | + import Alert from "../../components/Alert.svelte"; |
| 15 | + import SelectNodeId from "../../components/SelectNodeId.svelte"; |
| 16 | + import Modal from "../../components/DeploymentModal.svelte"; |
| 17 | + import hasEnoughBalance from "../../utils/hasEnoughBalance"; |
| 18 | + import validateName, { |
| 19 | + isInvalid, |
| 20 | + validateCpu, |
| 21 | + validateDisk, |
| 22 | + validateEmail, |
| 23 | + validateMemory, |
| 24 | + } from "../../utils/validateName"; |
| 25 | + import { noActiveProfile } from "../../utils/message"; |
| 26 | +
|
| 27 | +
|
| 28 | + const data = new Discourse(); |
| 29 | +
|
| 30 | + let loading = false; |
| 31 | + let success = false; |
| 32 | + let failed = false; |
| 33 | +
|
| 34 | + let status: "valid" | "invalid"; |
| 35 | + let profile: IProfile; |
| 36 | +
|
| 37 | + const deploymentStore = window.configs?.deploymentStore; |
| 38 | + const currentDeployment = window.configs?.currentDeploymentStore; |
| 39 | +
|
| 40 | + // prettier-ignore |
| 41 | + const tabs: ITab[] = [ |
| 42 | + { label: "Config", value: "config" }, |
| 43 | + { label: "Mail Server", value: "mail" }, |
| 44 | +
|
| 45 | + ]; |
| 46 | + let active = "config"; |
| 47 | +
|
| 48 | + // prettier-ignore |
| 49 | + const fields: IFormField[] = [ |
| 50 | + { label: "Name", symbol: "name", placeholder: "Discourse Instance Name", type: "text", validator: validateName, invalid: false }, |
| 51 | + { label: "CPU", symbol: "cpu", placeholder: "CPU", type: "number", validator: validateCpu, invalid: false }, |
| 52 | + { label: "Memory (MB)", symbol: 'memory', placeholder: "Memory in MB", type: "number", validator: validateMemory, invalid: false }, |
| 53 | + { label: "Disk Size (GB)", symbol: "diskSize", placeholder: "Disk Size in GB", type: "number", validator: validateDisk, invalid: false }, |
| 54 | + { label: "Email", symbol: "developerEmail", placeholder: "Admin Email", type: "text", validator: validateEmail, invalid: false }, |
| 55 | + { label: "Public IP", symbol: "publicIp", type: 'checkbox' }, |
| 56 | + { label: "Planetary Network", symbol: "planetary", placeholder: "Enable planetary network", type: 'checkbox' }, |
| 57 | + ]; |
| 58 | + |
| 59 | + $: disabled = ((loading || !data.valid) && !(success || failed)) || !profile || status !== "valid" || isInvalid(fields); // prettier-ignore |
| 60 | +
|
| 61 | + let message: string; |
| 62 | + let modalData: Object; |
| 63 | +
|
| 64 | + async function deployDiscourseHandler() { |
| 65 | + loading = true; |
| 66 | +
|
| 67 | + if (!hasEnoughBalance()) { |
| 68 | + failed = true; |
| 69 | + loading = false; |
| 70 | + message = |
| 71 | + "No enough balance to execute! Transaction requires 2 TFT at least in your wallet."; |
| 72 | + return; |
| 73 | + } |
| 74 | +
|
| 75 | + success = false; |
| 76 | + failed = false; |
| 77 | + message = undefined; |
| 78 | +
|
| 79 | + deployDiscourse(data, profile) |
| 80 | + .then((data: any) => { |
| 81 | + modalData = data; |
| 82 | + deploymentStore.set(0); |
| 83 | + success = true; |
| 84 | + }) |
| 85 | + .catch((err: string) => { |
| 86 | + failed = true; |
| 87 | + message = err; |
| 88 | + }) |
| 89 | + .finally(() => { |
| 90 | + loading = false; |
| 91 | + }); |
| 92 | + } |
| 93 | +
|
| 94 | + $: logs = $currentDeployment; |
| 95 | +</script> |
| 96 | + |
| 97 | +<SelectProfile |
| 98 | + on:profile={({ detail }) => { |
| 99 | + profile = detail; |
| 100 | + }} |
| 101 | +/> |
| 102 | + |
| 103 | +<div style="padding: 15px;"> |
| 104 | + <form class="box" on:submit|preventDefault={deployDiscourseHandler}> |
| 105 | + <h4 class="is-size-4 mb-4">Deploy a Discourse Instance</h4> |
| 106 | + |
| 107 | + <hr /> |
| 108 | + |
| 109 | + {#if loading || (logs !== null && logs.type === "Discourse")} |
| 110 | + <Alert type="info" message={logs?.message ?? "Loading..."} /> |
| 111 | + {:else if !profile} |
| 112 | + <Alert type="info" message={noActiveProfile} /> |
| 113 | + {:else if success} |
| 114 | + <Alert |
| 115 | + type="success" |
| 116 | + message="Successfully Deployed Discourse." |
| 117 | + deployed={true} |
| 118 | + /> |
| 119 | + {:else if failed} |
| 120 | + <Alert type="danger" message={message || "Failed to Deploy Discourse."} /> |
| 121 | + {:else} |
| 122 | + <Tabs bind:active {tabs} /> |
| 123 | + |
| 124 | + {#if active === "config"} |
| 125 | + {#each fields as field (field.symbol)} |
| 126 | + {#if field.invalid !== undefined} |
| 127 | + <Input |
| 128 | + bind:data={data[field.symbol]} |
| 129 | + bind:invalid={field.invalid} |
| 130 | + {field} |
| 131 | + /> |
| 132 | + {:else} |
| 133 | + <Input bind:data={data[field.symbol]} {field} /> |
| 134 | + {/if} |
| 135 | + {/each} |
| 136 | + |
| 137 | + <SelectNodeId |
| 138 | + cpu={data.cpu} |
| 139 | + memory={data.memory} |
| 140 | + publicIp={data.publicIp} |
| 141 | + ssd={data.diskSize + rootFs(data.cpu, data.memory)} |
| 142 | + bind:data={data.nodeId} |
| 143 | + bind:nodeSelection={data.selection.type} |
| 144 | + bind:status |
| 145 | + filters={data.selection.filters} |
| 146 | + {profile} |
| 147 | + on:fetch={({ detail }) => (data.selection.nodes = detail)} |
| 148 | + nodes={data.selection.nodes} |
| 149 | + /> |
| 150 | + |
| 151 | + <!-- SMTP fields --> |
| 152 | + {:else if active === "mail"} |
| 153 | + <div class="notification is-warning is-light"> |
| 154 | + <p> |
| 155 | + Discourse needs SMTP service so please configure these settings properly. |
| 156 | + </p> |
| 157 | + </div> |
| 158 | + {#each data.smtp.fields as field (field.symbol)} |
| 159 | + {#if field.invalid !== undefined} |
| 160 | + <Input |
| 161 | + bind:data={data.smtp[field.symbol]} |
| 162 | + bind:invalid={field.invalid} |
| 163 | + {field} |
| 164 | + /> |
| 165 | + {:else} |
| 166 | + <Input bind:data={data.smtp[field.symbol]} {field} /> |
| 167 | + {/if} |
| 168 | + {/each} |
| 169 | + {/if} |
| 170 | + {/if} |
| 171 | + |
| 172 | + <DeployBtn |
| 173 | + {disabled} |
| 174 | + {loading} |
| 175 | + {success} |
| 176 | + {failed} |
| 177 | + on:click={(e) => { |
| 178 | + if (success || failed) { |
| 179 | + e.preventDefault(); |
| 180 | + success = false; |
| 181 | + failed = false; |
| 182 | + loading = false; |
| 183 | + } |
| 184 | + }} |
| 185 | + /> |
| 186 | + </form> |
| 187 | +</div> |
| 188 | + |
| 189 | +{#if modalData} |
| 190 | + <Modal data={modalData} on:closed={() => (modalData = null)} /> |
| 191 | +{/if} |
| 192 | + |
| 193 | +<style lang="scss" scoped> |
| 194 | + @import url("https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css"); |
| 195 | +</style> |
0 commit comments