1import { html, LitElement } from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'; 2 3const formattedUS = new Intl.NumberFormat('en-US', { 4 minimumFractionDigits: 3, 5 maximumFractionDigits: 3, 6}); 7 8const HOURS_PER_MONTH = 24 * 30; 9 10class ServerCalculator extends LitElement { 11 static properties = { 12 serverSize: { type: String }, 13 serverBackups: { type: Boolean }, 14 traffic: { type: Number }, 15 }; 16 17 constructor() { 18 super(); 19 this.serverSize = 'cores_4'; 20 this.serverBackups = false; 21 this.traffic = 1000; 22 this.pricePerTrafficGBMonth = 0.02; 23 } 24 25 createRenderRoot() { 26 return this; 27 } 28 29 serverSizeOptions = [ 30 { value: 'cores_4', label: '4 Cores, 8 GB RAM, 160 GB SSD' }, 31 { value: 'cores_6', label: '6 Cores, 16 GB RAM, 320 GB SSD' }, 32 { value: 'cores_8', label: '8 Cores, 32 GB RAM, 640 GB SSD' }, 33 { value: 'cores_16', label: '16 Cores, 64 GB RAM, 1280 GB SSD' }, 34 ]; 35 36 getPricing() { 37 let hourlyMachine = 0; 38 let hourlyBackup = 0; 39 let includedTraffic = 0; 40 41 switch (this.serverSize) { 42 case 'cores_4': 43 hourlyMachine = 0.12; 44 includedTraffic = 4000; 45 break; 46 case 'cores_6': 47 hourlyMachine = 0.24; 48 includedTraffic = 5000; 49 break; 50 case 'cores_8': 51 hourlyMachine = 0.48; 52 includedTraffic = 6000; 53 break; 54 case 'cores_16': 55 hourlyMachine = 0.96; 56 includedTraffic = 10000; 57 break; 58 } 59 60 if (this.serverBackups) { 61 hourlyBackup = hourlyMachine * 0.5; 62 } 63 64 const trafficMonthly = Math.max(0, this.traffic - includedTraffic) * this.pricePerTrafficGBMonth; 65 const trafficHourly = trafficMonthly / HOURS_PER_MONTH; 66 67 const hourlyTotal = hourlyMachine + hourlyBackup + trafficHourly; 68 const monthlyMachine = hourlyMachine * HOURS_PER_MONTH; 69 const monthlyBackup = hourlyBackup * HOURS_PER_MONTH; 70 const monthlyTotal = monthlyMachine + monthlyBackup + trafficMonthly; 71 72 return { 73 hourlyBackup, 74 hourlyMachine, 75 hourlyTotal, 76 hourlyTraffic: trafficHourly, 77 includedTraffic, 78 monthlyMachine, 79 monthlyTotal, 80 monthlyTraffic: trafficMonthly, 81 }; 82 } 83 84 render() { 85 const pricing = this.getPricing(); 86 87 return html` 88<div class="pricing-options pricing-section row"> 89 <div class="pricing-option col-6"> 90 <h5>Choose your Server Size</h5> 91 <form class="mt-8"> 92 <div class="mb-3"> 93 <label class="form-label" for="serverSize">Server Size</label> 94 <select class="form-select" id="serverSize" .value=${this.serverSize} @input=${e => this.serverSize = e.target.value}> 95 ${this.serverSizeOptions.map(option => html` 96 <option value=${option.value} ?selected=${this.serverSize === option.value}>${option.label}</option> 97 `)} 98 </select> 99 </div> 100 <div class="mb-3"> 101 <input type="checkbox" class="form-check-input" id="serverBackups" .checked=${this.serverBackups} @input=${e => this.serverBackups = e.target.checked}> 102 <label class="form-check-label" for="serverBackups">Automatic Backups</label> 103 </div> 104 105 <hr class="mt-4 mb-4"> 106 107 <div class="mb-3"> 108 <label for="traffic" class="form-label w-100">Traffic (${pricing.includedTraffic} GB included) <strong class="float-end"> ${this.traffic} GB</strong ></label> 109 <input type="range" class="form-range" id="storage" min="100" max="50000" step="10" .value=${this.traffic} @input=${e => this.traffic = parseInt(e.target.value)}> 110 </div> 111 </form> 112 </div> 113 <div class="pricing-option pricing-option-button col-6"> 114 <table class="table"> 115 <tr> 116 <td>Server</td> 117 <td class="text-end"><span>${formattedUS.format(pricing.hourlyMachine)}</span> € <small>/ hour</small></td> 118 <td class="text-end"><span>${formattedUS.format(pricing.monthlyMachine)}</span> € <small>/ month</small></td> 119 </tr> 120 <tr> 121 <td>Backup</td> 122 <td class="text-end"><span>${formattedUS.format(pricing.hourlyBackup)}</span> € <small>/ hour</small></td> 123 <td class="text-end"><span>${formattedUS.format(pricing.monthlyTraffic)}</span> € <small>/ month</small></td> 124 </tr> 125 <tr> 126 <td>Traffic</td> 127 <td class="text-end"><span>${formattedUS.format(pricing.hourlyTraffic)}</span> € <small>/ hour</small></td> 128 <td class="text-end"><span>${formattedUS.format(pricing.monthlyTraffic)}</span> € <small>/ month</small></td> 129 </tr> 130 <tr> 131 <th>Total</th> 132 <th class="text-end"><span>${formattedUS.format(pricing.hourlyTotal)}</span> € <small>/ hour</small></th> 133 <th class="text-end"><span>${formattedUS.format(pricing.monthlyTotal)}</span> € <small>/ month</small></th> 134 </tr> 135 </table> 136 137 <small>Prices are estimated and bases on an average of 30 days per month</small> 138 139 <div> 140 <a class="lead-button xl text-center pricing-toggle mt-8" href="https://hosting.squidex.io" target="_blank" rel="noopener">Create Server</a> 141 </div> 142 </div> 143</div>`; 144 } 145} 146 147customElements.define('server-calculator', ServerCalculator); 148 149class ClusterCalculator extends LitElement { 150 static properties = { 151 databaseCoreMemory: { type: Number }, 152 databaseCores: { type: Number }, 153 databaseCount: { type: Number }, 154 databaseDisk: { type: Number }, 155 storage: { type: Number }, 156 traffic: { type: Number }, 157 webCoreMemory: { type: Number }, 158 webCores: { type: Number }, 159 webCount: { type: Number }, 160 }; 161 162 constructor() { 163 super(); 164 this.databaseCoreMemory = 2; 165 this.databaseCores = 2; 166 this.databaseCount = 3; 167 this.databaseDisk = 100; 168 this.storage = 100; 169 this.webCoreMemory = 2; 170 this.webCores = 2; 171 this.webCount = 3; 172 this.traffic = 1000; 173 174 this.pricePerCore = 0.02; 175 this.pricePerDiskGBHour = 0.0005; 176 this.pricePerGBMemory = 0.01; 177 this.pricePerStorageGBMonth = 0.025; 178 this.pricePerTrafficGBMonth = 0.02; 179 } 180 181 createRenderRoot() { 182 return this; 183 } 184 185 getPricing() { 186 const webTotalCores = this.webCount * this.webCores; 187 const webTotalMemory = this.webCoreMemory * webTotalCores; 188 189 const databaseTotalCores = this.databaseCount * this.databaseCores; 190 const databaseTotalMemory = this.databaseCoreMemory * databaseTotalCores; 191 192 const storageMonthly = this.storage * this.pricePerStorageGBMonth; 193 const storageHourly = storageMonthly / HOURS_PER_MONTH; 194 195 const trafficMonthly = this.traffic * this.pricePerTrafficGBMonth; 196 const trafficHourly = trafficMonthly / HOURS_PER_MONTH; 197 198 const hourlyWeb = webTotalCores * this.pricePerCore + webTotalMemory * this.pricePerGBMemory; 199 const hourlyDatabase = databaseTotalMemory * this.pricePerCore + this.databaseCoreMemory * this.pricePerGBMemory + this.databaseDisk * this.pricePerDiskGBHour; 200 const hourlyTotal = hourlyWeb + hourlyDatabase + storageHourly + trafficHourly; 201 202 const monthlyWeb = hourlyWeb * HOURS_PER_MONTH; 203 const monthlyDatabase = hourlyDatabase * HOURS_PER_MONTH; 204 const monthlyTotal = monthlyWeb + monthlyDatabase + storageMonthly + trafficMonthly; 205 206 return { 207 hourlyDatabase, 208 hourlyTotal, 209 hourlyWeb, 210 monthlyDatabase, 211 monthlyTotal, 212 monthlyWeb, 213 storageHourly, 214 storageMonthly, 215 trafficHourly, 216 trafficMonthly, 217 }; 218 } 219 220 render() { 221 const pricing = this.getPricing(); 222 return html` 223<div class="pricing-options pricing-section row"> 224 <div class="pricing-option col-6"> 225 <h5>Cluster Configuration</h5> 226 <form class="mt-8"> 227 <div class="mb-4"> 228 <label for="webCount" class="form-label w-100">Number of Squidex Servers<strong class="float-end">${this.webCount}</strong></label> 229 <input type="range" class="form-range" id="webCount" min="2" max="20" step="1" .value=${this.webCount} @input=${e => this.webCount = parseInt(e.target.value)}> 230 231 <small>Requires two instances: one for background jobs.</small> 232 </div> 233 234 <div class="mb-3"> 235 <label for="webCores" class="form-label w-100">Cores per Squidex Server<strong class="float-end">${this.webCores}</strong></label> 236 <input type="range" class="form-range" id="webCores" min="1" max="6" step="1" .value=${this.webCores} @input=${e => this.webCores = parseInt(e.target.value)}> 237 </div> 238 <div class="mb-3"> 239 <label for="webCoreMemory" class="form-label w-100">Memory per Core<strong class="float-end">${this.webCoreMemory} GB</strong></label> 240 <input type="range" class="form-range" id="webCoreMemory" min="2" max="4" step="1" .value=${this.webCoreMemory} @input=${e => this.webCoreMemory = parseInt(e.target.value)}> 241 </div> 242 243 <hr class="mt-4 mb-4"> 244 245 <div class="mb-4"> 246 <label for="databaseCount" class="form-label w-100">Number of Database Servers (min 3)<strong class="float-end">${this.databaseCount}</strong></label> 247 <input type="range" class="form-range" id="databaseCount" min="3" max="20" step="1" .value=${this.databaseCount} @input=${e => this.databaseCount = parseInt(e.target.value)}> 248 249 <small>Requires three instances for replica set.</small> 250 </div> 251 <div class="mb-3"> 252 <label for="databaseCores" class="form-label w-100">Cores per Database Server<strong class="float-end">${this.databaseCores}</strong></label> 253 <input type="range" class="form-range" id="databaseCores" min="1" max="6" step="1" .value=${this.databaseCores} @input=${e => this.databaseCores = parseInt(e.target.value)}> 254 </div> 255 <div class="mb-3"> 256 <label for="databaseCoreMemory" class="form-label w-100">Memory per Core<strong class="float-end">${this.databaseCoreMemory} GB</strong></label> 257 <input type="range" class="form-range" id="databaseCoreMemory" min="2" max="4" step="1" .value=${this.databaseCoreMemory} @input=${e => this.databaseCoreMemory = parseInt(e.target.value)}> 258 </div> 259 <div class="mb-3"> 260 <label for="databaseDisk" class="form-label w-100">Disk Size per Database Server<strong class="float-end">${this.databaseDisk} GB</strong></label> 261 <input type="range" class="form-range" id="databaseDisk" min="50" max="1000" step="10" .value=${this.databaseDisk} @input=${e => this.databaseDisk = parseInt(e.target.value)}> 262 </div> 263 264 <hr class="mt-4 mb-4"> 265 266 <div class="mb-3"> 267 <label for="storage" class="form-label w-100">Shared Storage<strong class="float-end">${this.storage} GB</strong></label> 268 <input type="range" class="form-range" id="storage" min="0" max="2000" step="10" .value=${this.storage} @input=${e => this.storage = parseInt(e.target.value)}> 269 </div> 270 271 <hr class="mt-4 mb-4"> 272 273 <div class="mb-3"> 274 <label for="traffic" class="form-label w-100">Traffic<strong class="float-end">${this.traffic} GB</strong></label> 275 <input type="range" class="form-range" id="storage" min="100" max="50000" step="10" .value=${this.traffic} @input=${e => this.traffic = parseInt(e.target.value)}> 276 </div> 277 </form> 278 </div> 279 <div class="pricing-option col-6"> 280 <table class="table mt-6"> 281 <tr> 282 <td>Squidex</td> 283 <td class="text-end"><span>${formattedUS.format(pricing.hourlyWeb)}</span> € <small>/ hour</small></td> 284 <td class="text-end"><span>${formattedUS.format(pricing.monthlyWeb)}</span> € <small>/ month</small></td> 285 </tr> 286 <tr> 287 <td>
287Database</td> 288 <td class="text-end"><span>${formattedUS.format(pricing.hourlyDatabase)}</span> € <small>/ hour</small></td> 289 <td class="text-end"><span>${formattedUS.format(pricing.monthlyDatabase)}</span> € <small>/ month</small></td> 290 </tr> 291 <tr> 292 <td>Storage</td> 293 <td class="text-end"><span>${formattedUS.format(pricing.storageHourly)}</span> € <small>/ hour</small></td> 294 <td class="text-end"><span>${formattedUS.format(pricing.storageMonthly)}</span> € <small>/ month</small></td> 295 </tr> 296 <tr> 297 <td>Traffic</td> 298 <td class="text-end"><span>${formattedUS.format(pricing.trafficHourly)}</span> € <small>/ hour</small></td> 299 <td class="text-end"><span>${formattedUS.format(pricing.trafficMonthly)}</span> € <small>/ month</small></td> 300 </tr> 301 <tr> 302 <th>Total</th> 303 <th class="text-end"><span>${formattedUS.format(pricing.hourlyTotal)}</span> € <small>/ hour</small></th> 304 <th class="text-end"><span>${formattedUS.format(pricing.monthlyTotal)}</span> € <small>/ month</small></th> 305 </tr> 306 </table> 307 308 <small>Prices are estimated and bases on an average of 30 days per month</small> 309 310 <div class="mt-8"> 311 <a class="lead-button xl text-center pricing-toggle" disabled="true">Create Cluster (SOON)</a> 312 </div> 313 </div> 314</div>`; 315 } 316} 317 318customElements.define('cluster-calculator', ClusterCalculator);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.