@gbdx/devis 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29115,6 +29115,75 @@ async function fetchCaddyRouteCount() {
29115
29115
  return null;
29116
29116
  }
29117
29117
  }
29118
+ function collectCaddyHosts(match) {
29119
+ if (!Array.isArray(match)) return [];
29120
+ const hosts = [];
29121
+ for (const m of match) {
29122
+ const host = m == null ? void 0 : m.host;
29123
+ if (Array.isArray(host)) {
29124
+ for (const h of host) if (typeof h === "string") hosts.push(h);
29125
+ }
29126
+ }
29127
+ return [...new Set(hosts)];
29128
+ }
29129
+ function collectCaddyHandlers(handle) {
29130
+ const upstreams = [];
29131
+ const handlers = [];
29132
+ const walk = (arr) => {
29133
+ if (!Array.isArray(arr)) return;
29134
+ for (const h of arr) {
29135
+ if (!h || typeof h !== "object") continue;
29136
+ const obj = h;
29137
+ if (typeof obj.handler === "string") handlers.push(obj.handler);
29138
+ if (Array.isArray(obj.upstreams)) {
29139
+ for (const u of obj.upstreams) {
29140
+ const dial = u == null ? void 0 : u.dial;
29141
+ if (typeof dial === "string") upstreams.push(dial);
29142
+ }
29143
+ }
29144
+ if (Array.isArray(obj.routes)) {
29145
+ for (const r2 of obj.routes) walk(r2 == null ? void 0 : r2.handle);
29146
+ }
29147
+ }
29148
+ };
29149
+ walk(handle);
29150
+ return { upstreams: [...new Set(upstreams)], handlers: [...new Set(handlers)] };
29151
+ }
29152
+ function parseCaddyRoute(route) {
29153
+ const r2 = route ?? {};
29154
+ const id = typeof r2["@id"] === "string" ? r2["@id"] : null;
29155
+ let slug = null;
29156
+ let appName = null;
29157
+ if (id) {
29158
+ const parts = id.split(":");
29159
+ if (parts.length >= 3 && parts[1] === "route") {
29160
+ slug = parts[0];
29161
+ appName = parts.slice(2).join(":");
29162
+ }
29163
+ }
29164
+ const hosts = collectCaddyHosts(r2.match);
29165
+ const { upstreams, handlers } = collectCaddyHandlers(r2.handle);
29166
+ return { id, slug, appName, hosts, upstreams, handlers };
29167
+ }
29168
+ async function fetchCaddyRoutes() {
29169
+ var _a, _b;
29170
+ const body2 = await caddyGet("/config/");
29171
+ if (!body2) return [];
29172
+ let cfg;
29173
+ try {
29174
+ cfg = JSON.parse(body2);
29175
+ } catch {
29176
+ return [];
29177
+ }
29178
+ const servers = ((_b = (_a = cfg.apps) == null ? void 0 : _a.http) == null ? void 0 : _b.servers) ?? {};
29179
+ const out = [];
29180
+ for (const srv of Object.values(servers)) {
29181
+ if (Array.isArray(srv == null ? void 0 : srv.routes)) {
29182
+ for (const route of srv.routes) out.push(parseCaddyRoute(route));
29183
+ }
29184
+ }
29185
+ return out;
29186
+ }
29118
29187
  function caddyPaths() {
29119
29188
  const home = getGlobalContext().homeDir;
29120
29189
  return {
@@ -29355,6 +29424,75 @@ function statusLabel(info) {
29355
29424
  }
29356
29425
  function CaddyView({ caddy, onLogs, onChanged, openLogKey }) {
29357
29426
  var _a, _b;
29427
+ const [routesOpen, setRoutesOpen] = reactExports.useState(false);
29428
+ const [routes, setRoutes] = reactExports.useState(null);
29429
+ const [routesLoading, setRoutesLoading] = reactExports.useState(false);
29430
+ async function loadRoutes() {
29431
+ setRoutesLoading(true);
29432
+ try {
29433
+ setRoutes(await fetchCaddyRoutes());
29434
+ } catch (err) {
29435
+ notifications.show({ color: "red", message: String(err) });
29436
+ setRoutes([]);
29437
+ } finally {
29438
+ setRoutesLoading(false);
29439
+ }
29440
+ }
29441
+ function openRoutes() {
29442
+ setRoutesOpen(true);
29443
+ void loadRoutes();
29444
+ }
29445
+ function renderRoutesBody() {
29446
+ if (routesLoading && routes === null) {
29447
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { justify: "center", gap: "xs", p: "lg", children: [
29448
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Loader, { size: "sm" }),
29449
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "불러오는 중..." })
29450
+ ] });
29451
+ }
29452
+ const sorted = [...routes ?? []].sort((a, b) => {
29453
+ if (!!a.slug !== !!b.slug) return a.slug ? -1 : 1;
29454
+ const s = (a.slug ?? "").localeCompare(b.slug ?? "");
29455
+ return s !== 0 ? s : (a.appName ?? "").localeCompare(b.appName ?? "");
29456
+ });
29457
+ if (sorted.length === 0) {
29458
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", p: "md", children: "등록된 라우트가 없습니다." });
29459
+ }
29460
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { gap: "sm", children: [
29461
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { justify: "space-between", children: [
29462
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Text, { size: "sm", c: "dimmed", children: [
29463
+ "총 ",
29464
+ sorted.length,
29465
+ "개"
29466
+ ] }),
29467
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "compact-xs", variant: "subtle", loading: routesLoading, onClick: () => void loadRoutes(), children: "새로고침" })
29468
+ ] }),
29469
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Table, { striped: true, highlightOnHover: true, horizontalSpacing: "md", verticalSpacing: "xs", children: [
29470
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Thead, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29471
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Th, { children: "워크스페이스" }),
29472
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Th, { children: "앱" }),
29473
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Th, { children: "호스트" }),
29474
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Th, { children: "업스트림" })
29475
+ ] }) }),
29476
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Tbody, { children: sorted.map((r2, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29477
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: r2.slug ? /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "light", color: "grape", children: r2.slug }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "xs", c: "dimmed", children: "(기타)" }) }),
29478
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", children: r2.appName ?? (r2.handlers.join(", ") || "-") }) }),
29479
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: r2.hosts.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "-" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Stack, { gap: 2, children: r2.hosts.map((h) => /* @__PURE__ */ jsxRuntimeExports.jsx(
29480
+ Anchor,
29481
+ {
29482
+ size: "sm",
29483
+ onClick: (e) => {
29484
+ e.preventDefault();
29485
+ void openExternal(`https://${h}`);
29486
+ },
29487
+ children: h
29488
+ },
29489
+ h
29490
+ )) }) }),
29491
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: r2.upstreams.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "-" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Stack, { gap: 2, children: r2.upstreams.map((u) => /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { children: u }, u)) }) })
29492
+ ] }, r2.id ?? `route-${i}`)) })
29493
+ ] })
29494
+ ] });
29495
+ }
29358
29496
  async function act(action) {
29359
29497
  try {
29360
29498
  await caddyAction(action);
@@ -29419,73 +29557,100 @@ function CaddyView({ caddy, onLogs, onChanged, openLogKey }) {
29419
29557
  ] }) })
29420
29558
  ] });
29421
29559
  }
29422
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Stack, { gap: "xl", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SectionCard, { title: "Caddy (시스템 공유 HTTPS 리버스 프록시)", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Table, { verticalSpacing: "sm", horizontalSpacing: "md", layout: "fixed", className: classes$6.table, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tbody, { children: [
29423
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29424
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { w: 200, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "상태" }) }),
29425
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { gap: 8, children: [
29426
- /* @__PURE__ */ jsxRuntimeExports.jsx(StatusBadge, { status, label }),
29427
- ((_a = caddy.proc) == null ? void 0 : _a.pid) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Text, { size: "xs", c: "dimmed", children: [
29428
- "PID ",
29429
- caddy.proc.pid
29430
- ] }) : null,
29431
- ((_b = caddy.proc) == null ? void 0 : _b.restarts) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { size: "xs", variant: "light", color: "gray", children: [
29432
- "재시작 ",
29433
- caddy.proc.restarts,
29434
- "회"
29435
- ] }) : null
29436
- ] }) })
29437
- ] }),
29438
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29439
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "Admin API" }) }),
29440
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { gap: 8, children: [
29441
- /* @__PURE__ */ jsxRuntimeExports.jsx(
29442
- Anchor,
29443
- {
29444
- size: "sm",
29445
- onClick: (e) => {
29446
- e.preventDefault();
29447
- void openExternal(`${caddy.adminUrl}/config/`);
29448
- },
29449
- children: caddy.adminUrl
29450
- }
29451
- ),
29452
- caddy.reachable ? /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { size: "xs", color: "teal", variant: "light", children: "응답" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { size: "xs", color: "gray", variant: "light", children: "미응답" })
29453
- ] }) })
29454
- ] }),
29455
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29456
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "등록 라우트" }) }),
29457
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: caddy.routeCount === null ? /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "-" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { children: caddy.routeCount }) })
29458
- ] }),
29459
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29460
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "관리" }) }),
29461
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { gap: 6, children: [
29462
- /* @__PURE__ */ jsxRuntimeExports.jsx(
29463
- Button,
29464
- {
29465
- size: "compact-xs",
29466
- variant: "default",
29467
- onClick: () => onLogs({
29468
- streamKey: caddyStreamKey(),
29469
- title: "caddy (시스템)",
29470
- openStream: openCaddyLogStream,
29471
- actions: acts,
29472
- onClear: () => clearCaddyLogs(),
29473
- timestamped: false
29474
- }),
29475
- children: "로그"
29476
- }
29477
- ),
29478
- acts.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "compact-xs", variant: "light", color: a.color, onClick: a.run, children: a.label }, a.label))
29479
- ] }) })
29480
- ] }),
29481
- pathRow("데이터 디렉터리", caddy.dataDir),
29482
- pathRow("부트스트랩 config", caddy.bootstrapFile, true),
29483
- pathRow("로그 파일", caddy.logFile, true),
29484
- caddy.error ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29485
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "에러" }) }),
29486
- /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "red", children: caddy.error }) })
29487
- ] }) : null
29488
- ] }) }) }) });
29560
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { gap: "xl", children: [
29561
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SectionCard, { title: "Caddy (시스템 공유 HTTPS 리버스 프록시)", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Table, { verticalSpacing: "sm", horizontalSpacing: "md", layout: "fixed", className: classes$6.table, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tbody, { children: [
29562
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29563
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { w: 200, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "상태" }) }),
29564
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { gap: 8, children: [
29565
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusBadge, { status, label }),
29566
+ ((_a = caddy.proc) == null ? void 0 : _a.pid) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Text, { size: "xs", c: "dimmed", children: [
29567
+ "PID ",
29568
+ caddy.proc.pid
29569
+ ] }) : null,
29570
+ ((_b = caddy.proc) == null ? void 0 : _b.restarts) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { size: "xs", variant: "light", color: "gray", children: [
29571
+ "재시작 ",
29572
+ caddy.proc.restarts,
29573
+ "회"
29574
+ ] }) : null
29575
+ ] }) })
29576
+ ] }),
29577
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29578
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "Admin API" }) }),
29579
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { gap: 8, children: [
29580
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
29581
+ Anchor,
29582
+ {
29583
+ size: "sm",
29584
+ onClick: (e) => {
29585
+ e.preventDefault();
29586
+ void openExternal(`${caddy.adminUrl}/config/`);
29587
+ },
29588
+ children: caddy.adminUrl
29589
+ }
29590
+ ),
29591
+ caddy.reachable ? /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { size: "xs", color: "teal", variant: "light", children: "응답" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { size: "xs", color: "gray", variant: "light", children: "미응답" })
29592
+ ] }) })
29593
+ ] }),
29594
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29595
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "등록 라우트" }) }),
29596
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: caddy.routeCount === null ? /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "-" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { gap: 6, wrap: "nowrap", children: [
29597
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { children: caddy.routeCount }),
29598
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { label: "등록된 라우트 보기", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
29599
+ ActionIcon,
29600
+ {
29601
+ size: "sm",
29602
+ variant: "subtle",
29603
+ color: "gray",
29604
+ disabled: !caddy.routeCount,
29605
+ onClick: openRoutes,
29606
+ "aria-label": "등록된 라우트 보기",
29607
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(IconSearch, { size: 14 })
29608
+ }
29609
+ ) })
29610
+ ] }) })
29611
+ ] }),
29612
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29613
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "관리" }) }),
29614
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Group, { gap: 6, children: [
29615
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
29616
+ Button,
29617
+ {
29618
+ size: "compact-xs",
29619
+ variant: "default",
29620
+ onClick: () => onLogs({
29621
+ streamKey: caddyStreamKey(),
29622
+ title: "caddy (시스템)",
29623
+ openStream: openCaddyLogStream,
29624
+ actions: acts,
29625
+ onClear: () => clearCaddyLogs(),
29626
+ timestamped: false
29627
+ }),
29628
+ children: "로그"
29629
+ }
29630
+ ),
29631
+ acts.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "compact-xs", variant: "light", color: a.color, onClick: a.run, children: a.label }, a.label))
29632
+ ] }) })
29633
+ ] }),
29634
+ pathRow("데이터 디렉터리", caddy.dataDir),
29635
+ pathRow("부트스트랩 config", caddy.bootstrapFile, true),
29636
+ pathRow("로그 파일", caddy.logFile, true),
29637
+ caddy.error ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Table.Tr, { children: [
29638
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "dimmed", children: "에러" }) }),
29639
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Table.Td, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Text, { size: "sm", c: "red", children: caddy.error }) })
29640
+ ] }) : null
29641
+ ] }) }) }),
29642
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
29643
+ Modal,
29644
+ {
29645
+ opened: routesOpen,
29646
+ onClose: () => setRoutesOpen(false),
29647
+ title: "등록된 Caddy 라우트",
29648
+ size: "min(1000px, 90vw)",
29649
+ centered: true,
29650
+ children: renderRoutesBody()
29651
+ }
29652
+ )
29653
+ ] });
29489
29654
  }
29490
29655
  const bar = "_bar_8l5j4_5";
29491
29656
  const search = "_search_8l5j4_15";
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Devis</title>
7
- <script type="module" crossorigin src="./assets/index-CfF4ouPT.js"></script>
7
+ <script type="module" crossorigin src="./assets/index-0_fn9uMh.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="./assets/index-BDxL9RBN.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gbdx/devis",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Local development dashboard for pnpm monorepos — workspace scanning, process management, HTTPS proxy, Docker, and more.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "gibigspub@gmail.com",