Skip to content
Source
Sign inSign up

outdatedServerUpdate.ts

90 lines3.3 KB

1import type { EnvironmentId, ServerSelfUpdateInput } from "@t3tools/contracts";
2import * as Cause from "effect/Cause";
3import * as Effect from "effect/Effect";
4import * as Exit from "effect/Exit";
5import type * as HttpClient from "effect/http/HttpClient";
6import type { Atom } from "effect/reactivity";
7import type * as Socket from "effect/socket/Socket";
8​
9import { describeOutdatedHost, updateOutdatedHost } from "../connection/outdatedHostUpdate.ts";
10import type * as ConnectionResolver from "../connection/resolver.ts";
11import type * as EnvironmentRegistry from "../connection/registry.ts";
12import type * as RelayEnvironmentDiscovery from "../relay/discovery.ts";
13import { createAtomCommandScheduler, createRuntimeCommand } from "./runtime.ts";
14import {
15 serverUpdateFailureMessage,
16 serverUpdateStateAtom,
17 type ServerUpdateStage,
18} from "./server.ts";
19​
20export interface OutdatedServerUpdateTarget {
21 readonly environmentId: EnvironmentId;
22 readonly input: ServerSelfUpdateInput;
23 /** From the host descriptor when known; an outdated host never delivers a server config. */
24 readonly fromVersion?: string;
26​
27export function createOutdatedServerDescriptionCommand<E>(
28 runtime: Atom.AtomRuntime<
29 EnvironmentRegistry.EnvironmentRegistry | ConnectionResolver.ConnectionResolver,
30 E
31 >,
32) {
33 return createRuntimeCommand(runtime, {
34 label: "environment-data:server:describe-outdated-server",
35 execute: (environmentId: EnvironmentId) =>
36 describeOutdatedHost(environmentId).pipe(Effect.timeout("20 seconds")),
37 });
39​
40export function createOutdatedServerUpdateCommand<E>(
41 runtime: Atom.AtomRuntime<
42 | EnvironmentRegistry.EnvironmentRegistry
43 | ConnectionResolver.ConnectionResolver
44 | RelayEnvironmentDiscovery.RelayEnvironmentDiscovery
45 | Socket.WebSocketConstructor
46 | HttpClient.HttpClient,
47 E
48 >,
49) {
50 return createRuntimeCommand(runtime, {
51 label: "environment-data:server:update-outdated-server",
52 scheduler: createAtomCommandScheduler(),
53 concurrency: {
54 mode: "singleFlight",
55 key: ({ environmentId }: OutdatedServerUpdateTarget) => environmentId,
56 },
57 execute: Effect.fn("ServerUpdate.updateOutdatedServer")(function (
58 target: OutdatedServerUpdateTarget,
59 atomRegistry,
60 ) {
61 const stateAtom = serverUpdateStateAtom(target.environmentId);
62 const targetVersion = target.input.targetVersion;
63 const fromVersion = target.fromVersion ?? targetVersion;
64 let currentStage: ServerUpdateStage = "downloading";
65 const setStage = (stage: ServerUpdateStage) =>
66 Effect.sync(() => {
67 currentStage = stage;
68 atomRegistry.set(stateAtom, { status: "running", stage, fromVersion, targetVersion });
69 });
70 return setStage(currentStage).pipe(
71 Effect.andThen(updateOutdatedHost(target.environmentId, target.input, setStage)),
72 Effect.onExit((exit) =>
73 Effect.sync(() => {
74 if (Exit.isSuccess(exit) || Cause.hasInterruptsOnly(exit.cause)) {
75 atomRegistry.set(stateAtom, { status: "idle" });
76 return;
77 }
78 atomRegistry.set(stateAtom, {
79 status: "failed",
80 stage: currentStage,
81 fromVersion,
82 targetVersion,
83 message: serverUpdateFailureMessage(Cause.squash(exit.cause)),
84 });
85 }),
86 ),
87 );
88 }),
89 });