authorgravatar for 91240327+shivamhwp@users.noreply.github.comshivam <91240327+shivamhwp@users.noreply.github.com> 2026-10-14 09:31:34+05:30
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-14 09:31:34+05:30
log6002c5c68c637de2cd2557bd6c40e46c153e39b1
tree1e024d6380f3ea23489bbd885dab6e7045804da4
parent11fd54fcbbfb39f824bb4c908d428c20d801267c
signature Signed by PGP key B5690EEEBB952194

fix(server): usage hubs with a host-less URL no longer vanish from Limits (#11368)

Co-authored-by: PR Batch Tester <agent@local.test> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

2 files changed, 88 insertions(+), 2 deletions(-)

apps/server/src/usage/UsageLimitSources.test.ts created+85
......@@ -0,0 +1,85 @@
1import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
2import { expect, it } from "@effect/vitest";
3import { ServerConfigStreamEvent, UsageLimitSourceId } from "@t3tools/contracts";
4import * as Effect from "effect/Effect";
5import * as Layer from "effect/Layer";
6import * as Schema from "effect/Schema";
7import { HttpClient } from "effect/http";
8
9import * as BackgroundPolicy from "../background/BackgroundPolicy.ts";
10import * as ServerSettings from "../serverSettings.ts";
11import * as UsageLimitSources from "./UsageLimitSources.ts";
12
13const encodeConfigEvent = Schema.encodeSync(ServerConfigStreamEvent);
14
15it.effect("keeps malformed source URLs encodable for config subscribers", () =>
16 Effect.gen(function* () {
17 const sources = yield* UsageLimitSources.make;
18 yield* sources.refresh;
19 const snapshots = yield* sources.current;
20 expect(snapshots.map((source) => source.label)).toEqual([
21 "local-hub",
22 "opaque-hub",
23 "Named hub",
24 "hub.test:8317",
25 "Usage limit source",
26 ]);
27 expect(() =>
28 encodeConfigEvent({
29 version: 1,
30 type: "usageLimitSourcesUpdated",
31 payload: { sources: snapshots },
32 }),
33 ).not.toThrow();
34 expect(snapshots.every((source) => source.error === "No management key configured.")).toBe(
35 true,
36 );
37 }).pipe(
38 Effect.provide([
39 ServerSettings.layerTest({
40 usageLimitSources: {
41 [UsageLimitSourceId.make("local-hub")]: {
42 kind: "cliproxy",
43 url: "localhost:8317",
44 managementKey: "",
45 enabled: true,
46 },
47 [UsageLimitSourceId.make("opaque-hub")]: {
48 kind: "cliproxy",
49 url: "file:///tmp/hub",
50 managementKey: "",
51 enabled: true,
52 },
53 [UsageLimitSourceId.make("named-hub")]: {
54 kind: "cliproxy",
55 url: "localhost:8317",
56 label: "Named hub",
57 managementKey: "",
58 enabled: true,
59 },
60 [UsageLimitSourceId.make("valid-hub")]: {
61 kind: "cliproxy",
62 url: "https://hub.test:8317",
63 managementKey: "",
64 enabled: true,
65 },
66 [UsageLimitSourceId.make(" ")]: {
67 kind: "cliproxy",
68 url: "localhost:8317",
69 managementKey: "",
70 enabled: true,
71 },
72 },
73 }),
74 Layer.mock(BackgroundPolicy.BackgroundPolicy)({
75 shouldRunScopeWork: () => Effect.succeed(false),
76 }),
77 Layer.succeed(
78 HttpClient.HttpClient,
79 HttpClient.make(() => Effect.die("Unexpected network request")),
80 ),
81 NodeCrypto.layer,
82 ]),
83 Effect.scoped,
84 ),
85);
apps/server/src/usage/UsageLimitSources.ts+3-2
......@@ -53,10 +53,11 @@ export class UsageLimitSources extends Context.Service<
5353
5454function sourceLabel(id: string, config: UsageLimitSourceConfig): string {
5555 if (config.label) return config.label;
56 const fallback = id.trim() || "Usage limit source";
5657 try {
57 return new URL(config.url).host;
58 return new URL(config.url).host || fallback;
5859 } catch {
59 return id;
60 return fallback;
6061 }
6162}
6263