Path: blob/master/src/packages/frontend/compute/idle-timeout.tsx
1503 views
import { useState } from "react";1import { useServer } from "./compute-server";2import { TimeAgo } from "@cocalc/frontend/components/time-ago";3import dayjs from "dayjs";4import { InputNumber, Tooltip } from "antd";5import { useInterval } from "react-interval-hook";6import { IDLE_TIMEOUT_MINUTES_DEFAULT } from "@cocalc/util/db-schema/compute-servers";7import { AutomaticShutdownCard } from "./automatic-shutdown";8import { setServerConfiguration } from "./api";9import duration from "dayjs/plugin/duration";10dayjs.extend(duration);1112export function IdleTimeout({13id,14project_id,15help,16}: {17id: number;18project_id: string;19help?: boolean;20}) {21const server = useServer({ id, project_id });22const [error, setError] = useState<string>("");23const [saving, setSaving] = useState<boolean>(false);24const [enabled, setEnabled] = useState<boolean>(25!!server.configuration?.idleTimeoutMinutes,26);27const [idleTimeoutMinutes, setIdleTimeoutMinutes] = useState<number | null>(28server.configuration?.idleTimeoutMinutes ?? null,29);3031return (32<AutomaticShutdownCard33title="Idle Timeout"34icon="stopwatch"35setEnabled={(enabled) => {36setEnabled(enabled);37if (enabled) {38setIdleTimeoutMinutes(IDLE_TIMEOUT_MINUTES_DEFAULT);39} else {40setIdleTimeoutMinutes(0);41}42}}43save={async () => {44await setServerConfiguration({45id,46configuration: {47idleTimeoutMinutes: idleTimeoutMinutes ?? undefined,48},49});50}}51hasUnsavedChanges={52(server.configuration?.idleTimeoutMinutes ?? 0) !=53(idleTimeoutMinutes ?? 0)54}55savedEnabled={!!server.configuration?.idleTimeoutMinutes}56enabled={enabled}57saving={saving}58setSaving={setSaving}59error={error}60setError={setError}61>62<IdleTimeoutMessage minimal project_id={project_id} id={id} />63{help && (64<div style={{ marginBottom: "15px" }}>65<p>66<IdleTimeoutMessage project_id={project_id} id={id} />67</p>68<p>69Automatically stop the compute server if no terminal or file (e.g.,70Jupyter notebook) on this compute server is used through the main71CoCalc web interface for a given numbers of minutes. CPU and GPU72usage is not taken into account.73</p>74<ul>75<li>76Idle timeout for compute servers has no direct impact on their77cost. Indirectly, setting an idle timeout can save you a huge78amount of money, depending on your usage patterns!79</li>80<li>81Compute server idle timeout is unrelated to your home base's idle82timeout. Any time a compute server is running, it keeps the home83base project running, which effectively gives the home base a long84idle timeout.85</li>86</ul>87</div>88)}89<div style={{ textAlign: "center" }}>90<InputNumber91style={{ width: "300px" }}92disabled={saving || !enabled}93min={0}94step={15}95value={idleTimeoutMinutes}96onChange={(value) => setIdleTimeoutMinutes(value)}97addonAfter="timeout minutes"98placeholder="Idle timeout..."99/>100</div>101</AutomaticShutdownCard>102);103}104105interface Props {106id: number;107project_id: string;108style?;109minimal?: boolean;110}111112export function IdleTimeoutMessage({ id, project_id, style, minimal }: Props) {113const server = useServer({ id, project_id });114const [counter, setCounter] = useState<number>(0);115useInterval(() => {116setCounter(counter + 1);117}, 5000);118119if (!server) {120return null;121}122const { state, last_edited_user } = server;123const idleTimeoutMinutes = server.configuration?.idleTimeoutMinutes;124if (!idleTimeoutMinutes || state != "running" || !last_edited_user) {125return null;126}127const last = dayjs(last_edited_user);128const date = last.add(idleTimeoutMinutes, "minutes");129const mesg = (130<>131Server will stop <TimeAgo date={date.toDate()} /> unlesss somebody132actively edits.133</>134);135if (!minimal) {136return <div style={style}>{mesg}</div>;137}138139let d = date.diff(dayjs());140const formattedDiff = dayjs.duration(d).format("HH:mm:ss");141return (142<Tooltip title={<>Idle Timeout: {mesg}</>}>143<div style={style}>{formattedDiff}</div>144</Tooltip>145);146}147148149