Path: blob/master/src/packages/frontend/compute/cloud/common/dns.tsx
1503 views
import { DNS_COST_PER_HOUR, checkValidDomain } from "@cocalc/util/compute/dns";1import { useTypedRedux } from "@cocalc/frontend/app-framework";2import { useEffect, useMemo, useState } from "react";3import { Alert, Button, Checkbox, Flex, Input, Spin, Switch } from "antd";4import { A, Icon } from "@cocalc/frontend/components";5import { currency } from "@cocalc/util/misc";6import { debounce } from "lodash";7import { isDnsAvailable } from "@cocalc/frontend/compute/api";8import { reuseInFlight } from "@cocalc/util/reuse-in-flight";9import ShowError from "@cocalc/frontend/components/error";1011async function checkDns(dns, setDnsError, setChecking) {12try {13checkValidDomain(dns);14} catch (err) {15setDnsError(`${err}.16Please enter a valid subdomain name. Subdomains can consist of17letters (a-z, A-Z), numbers (0-9), and hyphens (-). They18cannot start or end with a hyphen.`);19return false;20}21try {22setChecking(true);23if (!(await isDnsAvailable(dns))) {24setDnsError(25`${dns} is not available -- in use by another compute server`,26);27return false;28}29setDnsError("");30return true;31} catch (err) {32setDnsError(`${err}`);33} finally {34setChecking(false);35}36}3738export default function DNS({ setConfig, configuration, loading }) {39const compute_servers_dns = useTypedRedux("customize", "compute_servers_dns");40const [help, setHelp] = useState<boolean>(false);41const [showDns, setShowDns] = useState<boolean>(!!configuration.dns);42const [dnsError, setDnsError] = useState<string>("");43const [dns, setDns] = useState<string | undefined>(configuration.dns);44const [checking, setChecking] = useState<boolean>(false);45const checkValid = useMemo(() => {46const f = reuseInFlight(async (dns) => {47await checkDns(dns, setDnsError, setChecking);48});49return debounce(f, 1000);50}, [setDnsError]);5152useEffect(() => {53if (dns) {54checkValid(dns);55}56}, [dns]);5758if (!compute_servers_dns) {59return null;60}6162return (63<div>64<Flex style={{ alignItems: "center" }}>65<Checkbox66style={{ flex: 1 }}67disabled={loading}68checked={showDns}69onChange={() => {70setShowDns(!showDns);71if (showDns) {72// disable on backend.73setConfig({ dns: "" });74}75}}76>77DNS: Custom Subdomain with SSL ({currency(DNS_COST_PER_HOUR)}/hour78when running or stopped)79</Checkbox>{" "}80{showDns && (81<Switch82size="small"83checkedChildren={"Help"}84unCheckedChildren={"Help"}85style={{ float: "right" }}86checked={help}87onChange={(val) => setHelp(val)}88/>89)}90</Flex>9192{showDns && (93<div style={{ marginTop: "5px" }}>94<Flex style={{ alignItems: "center" }}>95<Input96disabled={loading}97style={{ margin: "15px 50px 15px 0", flex: 0.5 }}98maxLength={63}99showCount100allowClear101value={dns}102onChange={(e) => {103const dns = e.target.value.trim();104setDns(dns);105if (!dns) {106setConfig({ dns: "" });107}108}}109/>110{showDns && (111<A112style={{ flex: 0.5 }}113href={`https://${configuration.dns}.${compute_servers_dns}`}114>115<Icon name="external-link" /> https://{dns ?? "*"}.116{compute_servers_dns}117</A>118)}119</Flex>120<Button121disabled={122configuration.dns == dns || dnsError || loading || checking123}124onClick={async () => {125if (await checkDns(dns, setDnsError, setChecking)) {126const s = (dns ?? "").toLowerCase();127setConfig({ dns: s });128setDns(s);129}130}}131>132{!dns || configuration.dns != dns133? "Enable Custom Domain"134: "Custom Domain Enabled"}135{checking && <Spin style={{ marginLeft: "15px" }} delay={500} />}136</Button>137{dns && dnsError && (138<ShowError139error={dnsError}140setError={setDnsError}141style={{ margin: "10px 0" }}142/>143)}144{help && (145<Alert146type="info"147style={{ margin: "10px 0" }}148showIcon149message={"Custom DNS Subdomain"}150description={151<>152<p>153A custom DNS A record with{" "}154<A href="https://developers.cloudflare.com/dns/manage-dns-records/reference/proxied-dns-records/">155https and http proxying will be created at CloudFlare156</A>{" "}157as long as your VM is not deprovisioned. Whenever your VM158starts running it is allocated an external ip address, and159CoCalc updates the DNS entry to point at that ip address. An160https web server that you run on your compute server161listening on port 443 with a self-signed certificate will162appear to have a valid certificate to browsers visiting the163above URL.164</p>165<ul>166<li> You can enable or disable custom DNS at any time.</li>167<li>168<b>NOTE:</b> Depending on your browser, JupyterLab and VS169Code may fail in random ways due to security restrictions170if you do not enable DNS.171</li>172<li>173<A href="https://youtu.be/Il6rkXaDfUA">174<Icon175name="youtube"176style={{177color: "white",178background: "#ff0100",179padding: "0 3px",180borderRadius: "5px",181marginRight: "5px",182}}183/>184Web Server Demo185</A>186</li>187</ul>188</>189}190/>191)}192</div>193)}194</div>195);196}197198199