@cartanova/qgrid-cli 2.6.7 → 2.6.9
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.
- package/bundle/dist/application/monit/monit.frame.js +2 -2
- package/bundle/dist/application/qgrid/expired-token-reminder.js +45 -15
- package/bundle/dist/application/qgrid/oauth.js +1 -1
- package/bundle/dist/application/qgrid/qgrid.frame.js +3 -3
- package/bundle/dist/application/qgrid/slack-user-map.js +5 -14
- package/bundle/dist/application/qgrid/token-death.js +3 -2
- package/bundle/dist/application/qgrid/tool-emulation.js +1 -1
- package/bundle/dist/application/queries.generated.js +8 -2
- package/bundle/dist/application/request-log/request-log.model.js +2 -2
- package/bundle/dist/application/request-log-step/request-log-step.model.js +1 -1
- package/bundle/dist/application/setting/setting.constant.js +226 -0
- package/bundle/dist/application/setting/setting.model.js +254 -0
- package/bundle/dist/application/setting/setting.store.js +66 -0
- package/bundle/dist/application/setting/setting.types.js +51 -0
- package/bundle/dist/application/sonamu.generated.js +32 -2
- package/bundle/dist/application/sonamu.generated.sso.js +12 -2
- package/bundle/dist/application/token/token.model.js +1 -1
- package/bundle/dist/i18n/en.js +3 -1
- package/bundle/dist/i18n/ko.js +3 -1
- package/bundle/dist/i18n/sd.generated.js +9 -1
- package/bundle/dist/migrations/20260808194708_create__settings.js +24 -0
- package/bundle/dist/sonamu.config.js +8 -2
- package/bundle/dist/utils/providers/openai/openai-worker-pool-config.js +21 -6
- package/bundle/dist/utils/quiet-hours.js +47 -0
- package/bundle/dist/utils/slack-notify.js +16 -5
- package/bundle/src/application/qgrid/expired-token-reminder.test.ts +147 -2
- package/bundle/src/application/qgrid/expired-token-reminder.ts +66 -16
- package/bundle/src/application/qgrid/slack-user-map.test.ts +23 -0
- package/bundle/src/application/qgrid/slack-user-map.ts +4 -14
- package/bundle/src/application/qgrid/token-death.ts +2 -0
- package/bundle/src/application/queries.generated.ts +21 -1
- package/bundle/src/application/setting/setting.constant.test.ts +109 -0
- package/bundle/src/application/setting/setting.constant.ts +250 -0
- package/bundle/src/application/setting/setting.entity.json +59 -0
- package/bundle/src/application/setting/setting.model.ts +277 -0
- package/bundle/src/application/setting/setting.store.test.ts +87 -0
- package/bundle/src/application/setting/setting.store.ts +85 -0
- package/bundle/src/application/setting/setting.types.ts +53 -0
- package/bundle/src/application/sonamu.generated.http +54 -0
- package/bundle/src/application/sonamu.generated.sso.ts +21 -0
- package/bundle/src/application/sonamu.generated.ts +51 -0
- package/bundle/src/i18n/en.ts +2 -0
- package/bundle/src/i18n/ko.ts +2 -0
- package/bundle/src/i18n/sd.generated.ts +8 -0
- package/bundle/src/migrations/20260808194708_create__settings.ts +19 -0
- package/bundle/src/sonamu.config.ts +12 -0
- package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +12 -0
- package/bundle/src/utils/providers/openai/openai-worker-pool-config.ts +43 -5
- package/bundle/src/utils/quiet-hours.ts +79 -0
- package/bundle/src/utils/slack-notify.test.ts +171 -20
- package/bundle/src/utils/slack-notify.ts +31 -4
- package/bundle/web-dist/client/assets/index-DnnliAzf.js +78 -0
- package/bundle/web-dist/client/assets/index-rdbxJinT.css +1 -0
- package/bundle/web-dist/client/assets/{logs-CUO9Q0UT.js → logs-5gACO5tJ.js} +1 -1
- package/bundle/web-dist/client/assets/{monit-Dy7UtqC2.js → monit-DRCc50GU.js} +1 -1
- package/bundle/web-dist/client/assets/plus-p4vtg6LN.js +1 -0
- package/bundle/web-dist/client/assets/{routes-B6gfQrph.js → routes-DBdI9Ba1.js} +2 -2
- package/bundle/web-dist/client/assets/{sd.generated-DYdwSB2I.js → sd.generated-Bwa7rses.js} +1 -1
- package/bundle/web-dist/client/assets/services.generated-BzwTuZRP.js +1 -0
- package/bundle/web-dist/client/assets/settings-B62_r1i4.js +1 -0
- package/bundle/web-dist/client/assets/{show-Bi0QYUVm.js → show-BCRrSc8-.js} +1 -1
- package/bundle/web-dist/client/assets/tokens-CDiXKYGf.js +1 -0
- package/bundle/web-dist/client/assets/{use-oauth-login-flow-Cx6BlvXM.js → use-oauth-login-flow-Qqg4koSg.js} +1 -1
- package/bundle/web-dist/client/index.html +3 -3
- package/bundle/web-dist/server/assets/{lazyRouteComponent-BaZaveNc.js → lazyRouteComponent-Ws7I6fL-.js} +2 -1
- package/bundle/web-dist/server/assets/{logs-DMOw3SjD.js → logs-C1rxLh8H.js} +4 -4
- package/bundle/web-dist/server/assets/{monit-UmcqHroX.js → monit-C8gx9Sar.js} +3 -2
- package/bundle/web-dist/server/assets/plus-BISXNqRU.js +19 -0
- package/bundle/web-dist/server/assets/react-dom-D7duWOwf.js +462 -0
- package/bundle/web-dist/server/assets/{routes-BfoQmYud.js → routes-ZW_Ih5Em.js} +11 -7
- package/bundle/web-dist/server/assets/{sd.generated-B0xqnHEo.js → sd.generated-LO-h1_16.js} +13 -461
- package/bundle/web-dist/server/assets/{services.generated-DqAKskAS.js → services.generated-CZIChgHV.js} +119 -2
- package/bundle/web-dist/server/assets/settings--aoatERH.js +382 -0
- package/bundle/web-dist/server/assets/{show-C-oSdzHX.js → show-BY27w9oi.js} +4 -4
- package/bundle/web-dist/server/assets/{tokens-Dkd_uXEk.js → tokens-DIObtTuZ.js} +17 -72
- package/bundle/web-dist/server/assets/{use-oauth-login-flow-YM1nsd-H.js → use-oauth-login-flow-V3k8PSqQ.js} +2 -2
- package/bundle/web-dist/server/entry-server.generated.js +54 -18
- package/package.json +1 -1
- package/skills/qgrid/references/cli-env-and-server-boot.md +17 -2
- package/skills/qgrid/references/openai-codex-runtime.md +1 -1
- package/skills/qgrid/references/sonamu-api-web-flow.md +2 -0
- package/skills/qgrid/references/token-auth-quota-lifecycle.md +2 -0
- package/bundle/web-dist/client/assets/index-B4GlN1IX.css +0 -1
- package/bundle/web-dist/client/assets/index-CV_rp3zE.js +0 -78
- package/bundle/web-dist/client/assets/services.generated-BsAvotjG.js +0 -1
- package/bundle/web-dist/client/assets/tokens-BAuYgBl6.js +0 -1
- /package/bundle/web-dist/server/assets/{cost-CSH_t17I.js → cost-DzjfVtUr.js} +0 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { Y as __toESM, g as require_react, h as require_jsx_runtime, m as useQueryClient } from "./sd.generated-LO-h1_16.js";
|
|
2
|
+
import { a as SettingService } from "./services.generated-CZIChgHV.js";
|
|
3
|
+
import { t as lucidePlus } from "./plus-BISXNqRU.js";
|
|
4
|
+
//#region ~icons/lucide/eye.jsx
|
|
5
|
+
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
6
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
7
|
+
var lucideEye = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
8
|
+
viewBox: "0 0 24 24",
|
|
9
|
+
width: "1.2em",
|
|
10
|
+
height: "1.2em",
|
|
11
|
+
...props,
|
|
12
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", {
|
|
13
|
+
fill: "none",
|
|
14
|
+
stroke: "currentColor",
|
|
15
|
+
strokeLinecap: "round",
|
|
16
|
+
strokeLinejoin: "round",
|
|
17
|
+
strokeWidth: 2,
|
|
18
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696a10.75 10.75 0 0 1 19.876 0a1 1 0 0 1 0 .696a10.75 10.75 0 0 1-19.876 0" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
|
|
19
|
+
cx: 12,
|
|
20
|
+
cy: 12,
|
|
21
|
+
r: 3
|
|
22
|
+
})]
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region ~icons/lucide/eye-off.jsx
|
|
27
|
+
var lucideEyeOff = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
28
|
+
viewBox: "0 0 24 24",
|
|
29
|
+
width: "1.2em",
|
|
30
|
+
height: "1.2em",
|
|
31
|
+
...props,
|
|
32
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", {
|
|
33
|
+
fill: "none",
|
|
34
|
+
stroke: "currentColor",
|
|
35
|
+
strokeLinecap: "round",
|
|
36
|
+
strokeLinejoin: "round",
|
|
37
|
+
strokeWidth: 2,
|
|
38
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575a1 1 0 0 1 0 .696a10.8 10.8 0 0 1-1.444 2.49m-6.41-.679a3 3 0 0 1-4.242-4.242" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151a1 1 0 0 1 0-.696a10.75 10.75 0 0 1 4.446-5.143M2 2l20 20" })]
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region ~icons/lucide/minus.jsx
|
|
43
|
+
var lucideMinus = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
44
|
+
viewBox: "0 0 24 24",
|
|
45
|
+
width: "1.2em",
|
|
46
|
+
height: "1.2em",
|
|
47
|
+
...props,
|
|
48
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
49
|
+
fill: "none",
|
|
50
|
+
stroke: "currentColor",
|
|
51
|
+
strokeLinecap: "round",
|
|
52
|
+
strokeLinejoin: "round",
|
|
53
|
+
strokeWidth: 2,
|
|
54
|
+
d: "M5 12h14"
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region ~icons/lucide/rotate-ccw.jsx
|
|
59
|
+
var lucideRotateCcw = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
60
|
+
viewBox: "0 0 24 24",
|
|
61
|
+
width: "1.2em",
|
|
62
|
+
height: "1.2em",
|
|
63
|
+
...props,
|
|
64
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", {
|
|
65
|
+
fill: "none",
|
|
66
|
+
stroke: "currentColor",
|
|
67
|
+
strokeLinecap: "round",
|
|
68
|
+
strokeLinejoin: "round",
|
|
69
|
+
strokeWidth: 2,
|
|
70
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 12a9 9 0 1 0 9-9a9.75 9.75 0 0 0-6.74 2.74L3 8" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 3v5h5" })]
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/components/qgrid/SettingsPanel.tsx
|
|
75
|
+
var GROUP_LABELS = {
|
|
76
|
+
openai: "OpenAI 워커",
|
|
77
|
+
slack: "Slack 알림",
|
|
78
|
+
slackConnection: "Slack 연결"
|
|
79
|
+
};
|
|
80
|
+
function maskValue(value) {
|
|
81
|
+
if (!value) return "";
|
|
82
|
+
if (value.length <= 12) return "•".repeat(value.length);
|
|
83
|
+
return `${value.slice(0, 8)}${"•".repeat(12)}${value.slice(-4)}`;
|
|
84
|
+
}
|
|
85
|
+
/** 정수·실수는 타이핑보다 증감이 빠르다. 범위도 버튼이 알아서 지킨다. */
|
|
86
|
+
function Stepper({ value, min, max, step, disabled, onChange }) {
|
|
87
|
+
const current = Number(value);
|
|
88
|
+
const valid = Number.isFinite(current);
|
|
89
|
+
const lower = min ?? Number.NEGATIVE_INFINITY;
|
|
90
|
+
const upper = max ?? Number.POSITIVE_INFINITY;
|
|
91
|
+
const adjust = (delta) => {
|
|
92
|
+
const next = Math.min(Math.max((valid ? current : min ?? 0) + delta, lower), upper);
|
|
93
|
+
onChange(String(Number(next.toFixed(2))));
|
|
94
|
+
};
|
|
95
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
96
|
+
className: "inline-flex items-center rounded-lg border border-sand-200/80 bg-white overflow-hidden",
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
99
|
+
type: "button",
|
|
100
|
+
"aria-label": "감소",
|
|
101
|
+
onClick: () => adjust(-step),
|
|
102
|
+
disabled: disabled || valid && current <= lower,
|
|
103
|
+
className: "size-7 grid place-items-center text-sand-500 hover:bg-sand-100 disabled:opacity-30 disabled:hover:bg-transparent transition-colors duration-150",
|
|
104
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideMinus, { className: "size-3" })
|
|
105
|
+
}),
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
107
|
+
value,
|
|
108
|
+
onChange: (e) => onChange(e.target.value),
|
|
109
|
+
disabled,
|
|
110
|
+
inputMode: "decimal",
|
|
111
|
+
className: "w-12 text-center text-[12px] tabular-nums text-sand-800 bg-transparent border-x border-sand-200/80 py-1 focus:outline-none focus:bg-sand-50"
|
|
112
|
+
}),
|
|
113
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
114
|
+
type: "button",
|
|
115
|
+
"aria-label": "증가",
|
|
116
|
+
onClick: () => adjust(step),
|
|
117
|
+
disabled: disabled || valid && current >= upper,
|
|
118
|
+
className: "size-7 grid place-items-center text-sand-500 hover:bg-sand-100 disabled:opacity-30 disabled:hover:bg-transparent transition-colors duration-150",
|
|
119
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucidePlus, { className: "size-3" })
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/** 고정 선택지. 자유 입력보다 고르는 편이 빠르고, 범위를 벗어난 값이 생기지 않는다. */
|
|
125
|
+
function PresetGroup({ value, presets, disabled, onChange }) {
|
|
126
|
+
const current = Number(value);
|
|
127
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
128
|
+
className: "segmented-control inline-flex",
|
|
129
|
+
children: presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
130
|
+
type: "button",
|
|
131
|
+
disabled,
|
|
132
|
+
"data-active": current === preset,
|
|
133
|
+
onClick: () => onChange(String(preset)),
|
|
134
|
+
className: "rounded-[6px] px-2.5 py-1 text-[11px] font-medium tabular-nums text-sand-500 transition-colors hover:text-sand-800 disabled:opacity-50 data-[active=true]:text-sand-900",
|
|
135
|
+
children: preset >= 60 ? `${preset / 60}시간` : `${preset}분`
|
|
136
|
+
}, preset))
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/** 값이 어디서 왔는지. 되돌림 버튼이 무엇을 지우는지 이 배지가 설명한다. */
|
|
140
|
+
function SourceBadge({ source }) {
|
|
141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
142
|
+
className: `text-[10px] px-1.5 py-0.5 rounded ${source === "db" ? "bg-sienna-50 text-sienna-600" : "bg-sand-100 text-sand-500"}`,
|
|
143
|
+
children: source === "db" ? "저장값" : source === "env" ? "환경변수" : "기본값"
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function Toggle({ on, disabled, onChange }) {
|
|
147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
148
|
+
type: "button",
|
|
149
|
+
role: "switch",
|
|
150
|
+
"aria-checked": on,
|
|
151
|
+
disabled,
|
|
152
|
+
onClick: () => onChange(!on),
|
|
153
|
+
className: `relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors duration-200 disabled:opacity-50 ${on ? "bg-sage-400" : "bg-sand-300"}`,
|
|
154
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform duration-200 ${on ? "translate-x-4.5" : "translate-x-0.5"}` })
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function SettingRow({ item }) {
|
|
158
|
+
const [draft, setDraft] = (0, import_react.useState)(null);
|
|
159
|
+
const [revealed, setRevealed] = (0, import_react.useState)(false);
|
|
160
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
161
|
+
const queryClient = useQueryClient();
|
|
162
|
+
const updateMutation = SettingService.useUpdateSettingMutation();
|
|
163
|
+
const resetMutation = SettingService.useResetSettingMutation();
|
|
164
|
+
const pending = updateMutation.isPending || resetMutation.isPending;
|
|
165
|
+
const value = draft ?? item.value;
|
|
166
|
+
const dirty = draft !== null && draft !== item.value;
|
|
167
|
+
const invalidate = () => queryClient.invalidateQueries({ queryKey: ["Setting", "getSettingList"] });
|
|
168
|
+
const commit = async (next) => {
|
|
169
|
+
setError(null);
|
|
170
|
+
try {
|
|
171
|
+
await updateMutation.mutateAsync({
|
|
172
|
+
key: item.key,
|
|
173
|
+
value: next
|
|
174
|
+
});
|
|
175
|
+
await invalidate();
|
|
176
|
+
setDraft(null);
|
|
177
|
+
} catch (e) {
|
|
178
|
+
setError(e.message);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const reset = async () => {
|
|
182
|
+
setError(null);
|
|
183
|
+
try {
|
|
184
|
+
await resetMutation.mutateAsync({ key: item.key });
|
|
185
|
+
await invalidate();
|
|
186
|
+
setDraft(null);
|
|
187
|
+
} catch (e) {
|
|
188
|
+
setError(e.message);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const isSecret = item.kind === "secret";
|
|
192
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
193
|
+
className: "px-4 py-3 sm:px-5",
|
|
194
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
195
|
+
className: "flex items-start gap-4",
|
|
196
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
197
|
+
className: "min-w-0 flex-1",
|
|
198
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
199
|
+
className: "flex items-center gap-2",
|
|
200
|
+
children: [
|
|
201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
202
|
+
className: "text-[13px] text-sand-800",
|
|
203
|
+
children: item.label
|
|
204
|
+
}),
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SourceBadge, { source: item.source }),
|
|
206
|
+
item.applies === "restart" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
207
|
+
className: "text-[10px] text-caution-500",
|
|
208
|
+
title: "저장해도 다음 재시작부터 반영됩니다",
|
|
209
|
+
children: "재시작 필요"
|
|
210
|
+
})
|
|
211
|
+
]
|
|
212
|
+
}), item.help && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
213
|
+
className: "mt-0.5 text-[11px] text-sand-500",
|
|
214
|
+
children: item.help
|
|
215
|
+
})]
|
|
216
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
217
|
+
className: "flex items-center gap-1.5 shrink-0",
|
|
218
|
+
children: [
|
|
219
|
+
item.kind === "boolean" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toggle, {
|
|
220
|
+
on: value === "true",
|
|
221
|
+
disabled: pending,
|
|
222
|
+
onChange: (next) => void commit(String(next))
|
|
223
|
+
}),
|
|
224
|
+
item.kind === "preset" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PresetGroup, {
|
|
225
|
+
value,
|
|
226
|
+
presets: item.presets,
|
|
227
|
+
disabled: pending,
|
|
228
|
+
onChange: (next) => void commit(next)
|
|
229
|
+
}),
|
|
230
|
+
(item.kind === "integer" || item.kind === "number") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stepper, {
|
|
231
|
+
value,
|
|
232
|
+
min: item.min,
|
|
233
|
+
max: item.max,
|
|
234
|
+
step: item.kind === "integer" ? 1 : .5,
|
|
235
|
+
disabled: pending,
|
|
236
|
+
onChange: setDraft
|
|
237
|
+
}),
|
|
238
|
+
(item.kind === "string" || isSecret) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
239
|
+
className: "relative",
|
|
240
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
241
|
+
value: isSecret && !revealed ? maskValue(value) : value,
|
|
242
|
+
onChange: (e) => setDraft(e.target.value),
|
|
243
|
+
onFocus: () => isSecret && setRevealed(true),
|
|
244
|
+
disabled: pending,
|
|
245
|
+
className: `w-48 sm:w-64 rounded-lg border border-sand-200/80 bg-white py-1.5 pl-2.5 text-[12px] text-sand-800 focus:outline-none focus:border-sienna-300 ${isSecret ? "pr-8 font-mono" : "pr-2.5"}`
|
|
246
|
+
}), isSecret && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
247
|
+
type: "button",
|
|
248
|
+
"aria-label": revealed ? "값 숨기기" : "값 보기",
|
|
249
|
+
onClick: () => setRevealed((r) => !r),
|
|
250
|
+
className: "absolute right-1.5 top-1/2 -translate-y-1/2 size-6 grid place-items-center text-sand-400 hover:text-sand-600 transition-colors duration-150",
|
|
251
|
+
children: revealed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideEyeOff, { className: "size-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideEye, { className: "size-3.5" })
|
|
252
|
+
})]
|
|
253
|
+
}),
|
|
254
|
+
dirty && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
255
|
+
type: "button",
|
|
256
|
+
onClick: () => void commit(value),
|
|
257
|
+
disabled: pending,
|
|
258
|
+
className: "px-2.5 py-1.5 text-[11px] font-medium rounded-lg bg-sienna-400 text-white hover:bg-sienna-500 disabled:opacity-50 transition-colors duration-150",
|
|
259
|
+
children: updateMutation.isPending ? "저장 중" : "저장"
|
|
260
|
+
}),
|
|
261
|
+
!dirty && item.source === "db" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
262
|
+
type: "button",
|
|
263
|
+
title: "저장값을 지웁니다 — 환경변수 또는 기본값으로 돌아갑니다",
|
|
264
|
+
onClick: () => void reset(),
|
|
265
|
+
disabled: pending,
|
|
266
|
+
className: "size-7 grid place-items-center rounded-lg text-sand-400 hover:text-sand-600 hover:bg-sand-100 disabled:opacity-50 transition-colors duration-150",
|
|
267
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideRotateCcw, { className: "size-3.5" })
|
|
268
|
+
})
|
|
269
|
+
]
|
|
270
|
+
})]
|
|
271
|
+
}), error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
272
|
+
className: "mt-1.5 text-[11px] text-red-500",
|
|
273
|
+
children: error
|
|
274
|
+
})]
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
/** 만료 알림을 주기를 기다리지 않고 지금 보낸다. 설정이 제대로 붙었는지 확인하는 용도. */
|
|
278
|
+
function TriggerReminderButton() {
|
|
279
|
+
const [result, setResult] = (0, import_react.useState)(null);
|
|
280
|
+
const mutation = SettingService.useTriggerExpiryReminderMutation();
|
|
281
|
+
const trigger = async () => {
|
|
282
|
+
setResult(null);
|
|
283
|
+
try {
|
|
284
|
+
const { sent } = await mutation.mutateAsync();
|
|
285
|
+
setResult(sent === 0 ? "재로그인이 필요한 토큰이 없습니다" : `${sent}건 발송`);
|
|
286
|
+
} catch (e) {
|
|
287
|
+
setResult(e.message);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
291
|
+
className: "flex items-center gap-2",
|
|
292
|
+
children: [result && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
293
|
+
className: "text-[11px] text-sand-500",
|
|
294
|
+
children: result
|
|
295
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
296
|
+
type: "button",
|
|
297
|
+
onClick: () => void trigger(),
|
|
298
|
+
disabled: mutation.isPending,
|
|
299
|
+
className: "px-2 py-1 text-[11px] rounded-lg border border-sand-200/80 text-sand-600 hover:bg-sand-100 disabled:opacity-50 transition-colors duration-150",
|
|
300
|
+
children: mutation.isPending ? "보내는 중" : "지금 보내기"
|
|
301
|
+
})]
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function SettingsPanel() {
|
|
305
|
+
const { data, isLoading } = SettingService.useSettingList();
|
|
306
|
+
if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
307
|
+
className: "space-y-4",
|
|
308
|
+
children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
309
|
+
className: "panel overflow-hidden",
|
|
310
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
311
|
+
className: "panel-header px-4 py-2.5 sm:px-5",
|
|
312
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-3 w-24 rounded bg-sand-200 animate-pulse" })
|
|
313
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
314
|
+
className: "divide-y divide-sand-100/80",
|
|
315
|
+
children: [
|
|
316
|
+
0,
|
|
317
|
+
1,
|
|
318
|
+
2
|
|
319
|
+
].map((j) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
320
|
+
className: "px-4 py-3 sm:px-5 flex items-center justify-between gap-4",
|
|
321
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-3 w-40 rounded bg-sand-100 animate-pulse" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-7 w-24 rounded-lg bg-sand-100 animate-pulse" })]
|
|
322
|
+
}, j))
|
|
323
|
+
})]
|
|
324
|
+
}, i))
|
|
325
|
+
});
|
|
326
|
+
const settings = data?.settings ?? [];
|
|
327
|
+
const runtime = data?.runtime ?? [];
|
|
328
|
+
const groups = [...new Set(settings.map((s) => s.group))];
|
|
329
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
330
|
+
className: "space-y-4",
|
|
331
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
332
|
+
className: "panel overflow-hidden",
|
|
333
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
334
|
+
className: "panel-header flex items-center justify-between px-4 py-2.5 sm:px-5",
|
|
335
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
336
|
+
className: "text-[13px] font-medium text-sand-700",
|
|
337
|
+
children: "환경"
|
|
338
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
339
|
+
className: "text-[11px] text-sand-400",
|
|
340
|
+
children: "기동 시점 값"
|
|
341
|
+
})]
|
|
342
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dl", {
|
|
343
|
+
className: "px-4 py-3 sm:px-5 grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-1.5",
|
|
344
|
+
children: runtime.map((info) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
345
|
+
className: "flex items-baseline justify-between gap-3 min-w-0",
|
|
346
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
|
|
347
|
+
className: "text-[11px] text-sand-500 shrink-0",
|
|
348
|
+
children: info.label
|
|
349
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
|
|
350
|
+
className: "text-[12px] font-mono text-sand-700 truncate",
|
|
351
|
+
children: info.value
|
|
352
|
+
})]
|
|
353
|
+
}, info.label))
|
|
354
|
+
})]
|
|
355
|
+
}), groups.map((group) => {
|
|
356
|
+
const items = settings.filter((s) => s.group === group);
|
|
357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
358
|
+
className: "panel overflow-hidden",
|
|
359
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
360
|
+
className: "panel-header flex items-center justify-between px-4 py-2.5 sm:px-5",
|
|
361
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
362
|
+
className: "text-[13px] font-medium text-sand-700",
|
|
363
|
+
children: GROUP_LABELS[group] ?? group
|
|
364
|
+
}), group === "slack" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TriggerReminderButton, {})]
|
|
365
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
366
|
+
className: "divide-y divide-sand-100/80",
|
|
367
|
+
children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingRow, { item }, item.key))
|
|
368
|
+
})]
|
|
369
|
+
}, group);
|
|
370
|
+
})]
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region src/routes/settings.tsx?tsr-split=component
|
|
375
|
+
function SettingsPage() {
|
|
376
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
377
|
+
className: "max-w-4xl mx-auto",
|
|
378
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingsPanel, {})
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
//#endregion
|
|
382
|
+
export { SettingsPage as component };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as RequestLogStepService, r as RequestLogService } from "./services.generated-
|
|
3
|
-
import { a as Link } from "./lazyRouteComponent-
|
|
1
|
+
import { K as __commonJSMin, Y as __toESM, g as require_react, h as require_jsx_runtime, q as __exportAll } from "./sd.generated-LO-h1_16.js";
|
|
2
|
+
import { i as RequestLogStepService, r as RequestLogService } from "./services.generated-CZIChgHV.js";
|
|
3
|
+
import { a as Link } from "./lazyRouteComponent-Ws7I6fL-.js";
|
|
4
4
|
import { t as Route } from "../entry-server.generated.js";
|
|
5
|
-
import { n as formatMicroUsd, t as cacheHitRate } from "./cost-
|
|
5
|
+
import { n as formatMicroUsd, t as cacheHitRate } from "./cost-DzjfVtUr.js";
|
|
6
6
|
import minpath from "node:path";
|
|
7
7
|
import minproc from "node:process";
|
|
8
8
|
import { fileURLToPath as urlToPath } from "node:url";
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Y as __toESM, g as require_react, h as require_jsx_runtime, m as useQueryClient } from "./sd.generated-LO-h1_16.js";
|
|
2
|
+
import { n as QgridService, o as TokenService } from "./services.generated-CZIChgHV.js";
|
|
3
3
|
import { i as Q, o as lucideKeyRound } from "../entry-server.generated.js";
|
|
4
|
-
import { n as OAuthCodeEntry, r as useUpdateTokenMutation, t as useOAuthLoginFlow } from "./use-oauth-login-flow-
|
|
5
|
-
|
|
6
|
-
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
7
|
-
var import_jsx_runtime = require_jsx_runtime();
|
|
8
|
-
var lucidePlus = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
9
|
-
viewBox: "0 0 24 24",
|
|
10
|
-
width: "1.2em",
|
|
11
|
-
height: "1.2em",
|
|
12
|
-
...props,
|
|
13
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
14
|
-
fill: "none",
|
|
15
|
-
stroke: "currentColor",
|
|
16
|
-
strokeLinecap: "round",
|
|
17
|
-
strokeLinejoin: "round",
|
|
18
|
-
strokeWidth: 2,
|
|
19
|
-
d: "M5 12h14m-7-7v14"
|
|
20
|
-
})
|
|
21
|
-
});
|
|
22
|
-
//#endregion
|
|
4
|
+
import { n as OAuthCodeEntry, r as useUpdateTokenMutation, t as useOAuthLoginFlow } from "./use-oauth-login-flow-V3k8PSqQ.js";
|
|
5
|
+
import { t as lucidePlus } from "./plus-BISXNqRU.js";
|
|
23
6
|
//#region src/components/qgrid/AddTokenModal.tsx
|
|
7
|
+
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
8
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
24
9
|
function AddTokenModal() {
|
|
25
10
|
const [open, setOpen] = (0, import_react.useState)(false);
|
|
26
11
|
const [name, setName] = (0, import_react.useState)("");
|
|
@@ -195,7 +180,6 @@ function TokenTable({ data, isLoading }) {
|
|
|
195
180
|
const removeMutation = QgridService.useRemoveTokenMutation();
|
|
196
181
|
const updateMutation = useUpdateTokenMutation();
|
|
197
182
|
const toggleMutation = QgridService.useToggleTokenMutation();
|
|
198
|
-
const oauth = useOAuthLoginFlow();
|
|
199
183
|
const invalidate = () => Promise.all([queryClient.invalidateQueries({ queryKey: ["Token"] }), queryClient.invalidateQueries({ queryKey: ["Qgrid"] })]);
|
|
200
184
|
const handleToggle = async (token) => {
|
|
201
185
|
await toggleMutation.mutateAsync({ id: token.id });
|
|
@@ -207,9 +191,6 @@ function TokenTable({ data, isLoading }) {
|
|
|
207
191
|
await invalidate();
|
|
208
192
|
setDeleteTarget(null);
|
|
209
193
|
};
|
|
210
|
-
const submitReloginCode = async (pastedCode) => {
|
|
211
|
-
if (await oauth.submitCode(pastedCode)) oauth.reset();
|
|
212
|
-
};
|
|
213
194
|
const openEdit = (token) => {
|
|
214
195
|
setEditTarget(token);
|
|
215
196
|
setEditName(token.name ?? "");
|
|
@@ -323,28 +304,17 @@ function TokenTable({ data, isLoading }) {
|
|
|
323
304
|
className: "px-3 py-3",
|
|
324
305
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
325
306
|
className: "flex items-center gap-1",
|
|
326
|
-
children: [
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
336
|
-
|
|
337
|
-
className: "p-1 rounded text-sand-400 hover:text-sienna-500 transition-colors duration-150",
|
|
338
|
-
onClick: () => openEdit(token),
|
|
339
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucidePencil, { className: "size-4" })
|
|
340
|
-
}),
|
|
341
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
342
|
-
type: "button",
|
|
343
|
-
className: "p-1 rounded text-sand-400 hover:text-red-500 transition-colors duration-150",
|
|
344
|
-
onClick: () => setDeleteTarget(token),
|
|
345
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideTrash2, { className: "size-4" })
|
|
346
|
-
})
|
|
347
|
-
]
|
|
307
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
308
|
+
type: "button",
|
|
309
|
+
className: "p-1 rounded text-sand-400 hover:text-sienna-500 transition-colors duration-150",
|
|
310
|
+
onClick: () => openEdit(token),
|
|
311
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucidePencil, { className: "size-4" })
|
|
312
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
313
|
+
type: "button",
|
|
314
|
+
className: "p-1 rounded text-sand-400 hover:text-red-500 transition-colors duration-150",
|
|
315
|
+
onClick: () => setDeleteTarget(token),
|
|
316
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideTrash2, { className: "size-4" })
|
|
317
|
+
})]
|
|
348
318
|
})
|
|
349
319
|
})
|
|
350
320
|
]
|
|
@@ -354,31 +324,6 @@ function TokenTable({ data, isLoading }) {
|
|
|
354
324
|
})
|
|
355
325
|
})
|
|
356
326
|
}),
|
|
357
|
-
oauth.codeEntry && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
358
|
-
className: "fixed inset-0 z-50 flex items-start justify-center pt-[15vh]",
|
|
359
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
360
|
-
className: "absolute inset-0 bg-sand-900/8 backdrop-blur-sm",
|
|
361
|
-
onClick: oauth.reset,
|
|
362
|
-
onKeyDown: () => {}
|
|
363
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
364
|
-
className: "relative panel shadow-xl w-full max-w-md mx-4",
|
|
365
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
366
|
-
className: "px-5 py-4 border-b border-sand-100/60",
|
|
367
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
|
|
368
|
-
className: "text-base font-medium text-sand-900",
|
|
369
|
-
children: "재로그인"
|
|
370
|
-
})
|
|
371
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
372
|
-
className: "px-5 py-4",
|
|
373
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OAuthCodeEntry, {
|
|
374
|
-
isPending: oauth.completeMutation.isPending,
|
|
375
|
-
isError: oauth.completeMutation.isError,
|
|
376
|
-
onSubmit: (code) => void submitReloginCode(code),
|
|
377
|
-
onRestart: oauth.reset
|
|
378
|
-
})
|
|
379
|
-
})]
|
|
380
|
-
})]
|
|
381
|
-
}),
|
|
382
327
|
editTarget && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
383
328
|
className: "fixed inset-0 z-50 flex items-start justify-center pt-[15vh]",
|
|
384
329
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as QgridService,
|
|
1
|
+
import { Y as __toESM, g as require_react, h as require_jsx_runtime, m as useQueryClient } from "./sd.generated-LO-h1_16.js";
|
|
2
|
+
import { n as QgridService, s as useMutation } from "./services.generated-CZIChgHV.js";
|
|
3
3
|
import { i as Q } from "../entry-server.generated.js";
|
|
4
4
|
//#region src/services/token/use-update-token-mutation.ts
|
|
5
5
|
function useUpdateTokenMutation() {
|