@syncular/console 0.0.4-26 → 0.0.6-100
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/dist/App.d.ts +3 -7
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +3 -3
- package/dist/App.js.map +1 -1
- package/dist/hooks/ConnectionContext.d.ts +4 -1
- package/dist/hooks/ConnectionContext.d.ts.map +1 -1
- package/dist/hooks/ConnectionContext.js +116 -28
- package/dist/hooks/ConnectionContext.js.map +1 -1
- package/dist/hooks/useConsoleApi.d.ts +11 -1
- package/dist/hooks/useConsoleApi.d.ts.map +1 -1
- package/dist/hooks/useConsoleApi.js +78 -0
- package/dist/hooks/useConsoleApi.js.map +1 -1
- package/dist/hooks/useLiveEvents.d.ts.map +1 -1
- package/dist/hooks/useLiveEvents.js +116 -3
- package/dist/hooks/useLiveEvents.js.map +1 -1
- package/dist/layout.d.ts +4 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +8 -7
- package/dist/layout.js.map +1 -1
- package/dist/lib/api.d.ts +1 -1
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +36 -4
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/types.d.ts +13 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/mount.d.ts +1 -0
- package/dist/mount.d.ts.map +1 -1
- package/dist/mount.js +1 -1
- package/dist/mount.js.map +1 -1
- package/dist/pages/Config.d.ts +3 -1
- package/dist/pages/Config.d.ts.map +1 -1
- package/dist/pages/Config.js +24 -17
- package/dist/pages/Config.js.map +1 -1
- package/dist/pages/Fleet.d.ts +3 -1
- package/dist/pages/Fleet.d.ts.map +1 -1
- package/dist/pages/Fleet.js +6 -3
- package/dist/pages/Fleet.js.map +1 -1
- package/dist/pages/Ops.js.map +1 -1
- package/dist/pages/Storage.d.ts +2 -0
- package/dist/pages/Storage.d.ts.map +1 -0
- package/dist/pages/Storage.js +103 -0
- package/dist/pages/Storage.js.map +1 -0
- package/dist/pages/Stream.d.ts.map +1 -1
- package/dist/pages/Stream.js +2 -3
- package/dist/pages/Stream.js.map +1 -1
- package/dist/pages/index.d.ts +1 -0
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +1 -0
- package/dist/pages/index.js.map +1 -1
- package/dist/routeTree.d.ts +1 -1
- package/dist/routeTree.d.ts.map +1 -1
- package/dist/routeTree.js +2 -0
- package/dist/routeTree.js.map +1 -1
- package/dist/routes/__root.d.ts +1 -1
- package/dist/routes/__root.d.ts.map +1 -1
- package/dist/routes/config.d.ts +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/fleet.d.ts +1 -1
- package/dist/routes/fleet.d.ts.map +1 -1
- package/dist/routes/index.d.ts +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/investigate-commit.d.ts +1 -1
- package/dist/routes/investigate-commit.d.ts.map +1 -1
- package/dist/routes/investigate-event.d.ts +1 -1
- package/dist/routes/investigate-event.d.ts.map +1 -1
- package/dist/routes/ops.d.ts +1 -1
- package/dist/routes/ops.d.ts.map +1 -1
- package/dist/routes/storage.d.ts +2 -0
- package/dist/routes/storage.d.ts.map +1 -0
- package/dist/routes/storage.js +9 -0
- package/dist/routes/storage.js.map +1 -0
- package/dist/routes/stream.d.ts +1 -1
- package/dist/routes/stream.d.ts.map +1 -1
- package/dist/static-server.d.ts.map +1 -1
- package/dist/static-server.js +6 -1
- package/dist/static-server.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +9 -9
- package/src/App.tsx +12 -10
- package/src/__tests__/static-server.test.ts +193 -0
- package/src/hooks/ConnectionContext.tsx +135 -29
- package/src/hooks/useConsoleApi.ts +103 -0
- package/src/hooks/useLiveEvents.ts +142 -4
- package/src/layout.tsx +35 -5
- package/src/lib/api.ts +38 -5
- package/src/lib/types.ts +17 -0
- package/src/mount.tsx +6 -1
- package/src/pages/Config.tsx +57 -49
- package/src/pages/Fleet.tsx +19 -17
- package/src/pages/Storage.tsx +277 -0
- package/src/pages/Stream.tsx +6 -3
- package/src/pages/index.ts +1 -0
- package/src/routeTree.ts +2 -0
- package/src/routes/storage.tsx +9 -0
- package/src/static-server.ts +12 -1
- package/src/styles/globals.css +4 -1
- package/web-dist/assets/index-D8JLMM1I.js +86 -0
- package/web-dist/assets/index-D_fQabjS.css +1 -0
- package/web-dist/console.css +1 -1
- package/web-dist/index.html +2 -2
- package/web-dist/site.webmanifest +2 -2
- package/web-dist/assets/index-CTkQp6YC.js +0 -86
- package/web-dist/assets/index-j_U2SoXa.css +0 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Badge,
|
|
3
|
+
Button,
|
|
4
|
+
Dialog,
|
|
5
|
+
DialogContent,
|
|
6
|
+
DialogFooter,
|
|
7
|
+
DialogHeader,
|
|
8
|
+
DialogTitle,
|
|
9
|
+
EmptyState,
|
|
10
|
+
Input,
|
|
11
|
+
SectionCard,
|
|
12
|
+
Spinner,
|
|
13
|
+
Table,
|
|
14
|
+
TableBody,
|
|
15
|
+
TableCell,
|
|
16
|
+
TableHead,
|
|
17
|
+
TableHeader,
|
|
18
|
+
TableRow,
|
|
19
|
+
} from '@syncular/ui';
|
|
20
|
+
import { useState } from 'react';
|
|
21
|
+
import {
|
|
22
|
+
useBlobDownload,
|
|
23
|
+
useBlobs,
|
|
24
|
+
useDeleteBlobMutation,
|
|
25
|
+
} from '../hooks/useConsoleApi';
|
|
26
|
+
|
|
27
|
+
function formatFileSize(bytes: number): string {
|
|
28
|
+
if (bytes === 0) return '0 B';
|
|
29
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
30
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
31
|
+
const value = bytes / 1024 ** i;
|
|
32
|
+
return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function formatDateTime(iso: string): string {
|
|
36
|
+
const parsed = Date.parse(iso);
|
|
37
|
+
if (!Number.isFinite(parsed)) return iso;
|
|
38
|
+
return new Date(parsed).toLocaleString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function Storage() {
|
|
42
|
+
const [prefixInput, setPrefixInput] = useState('');
|
|
43
|
+
const [activePrefix, setActivePrefix] = useState<string | undefined>(
|
|
44
|
+
undefined
|
|
45
|
+
);
|
|
46
|
+
const [cursor, setCursor] = useState<string | undefined>(undefined);
|
|
47
|
+
const [cursorHistory, setCursorHistory] = useState<string[]>([]);
|
|
48
|
+
const [deletingKey, setDeletingKey] = useState<string | null>(null);
|
|
49
|
+
|
|
50
|
+
const { data, isLoading, error } = useBlobs({
|
|
51
|
+
prefix: activePrefix,
|
|
52
|
+
cursor,
|
|
53
|
+
limit: 100,
|
|
54
|
+
});
|
|
55
|
+
const deleteMutation = useDeleteBlobMutation();
|
|
56
|
+
const download = useBlobDownload();
|
|
57
|
+
|
|
58
|
+
function handleFilter() {
|
|
59
|
+
const trimmed = prefixInput.trim();
|
|
60
|
+
setActivePrefix(trimmed.length > 0 ? trimmed : undefined);
|
|
61
|
+
setCursor(undefined);
|
|
62
|
+
setCursorHistory([]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function handleClearFilter() {
|
|
66
|
+
setPrefixInput('');
|
|
67
|
+
setActivePrefix(undefined);
|
|
68
|
+
setCursor(undefined);
|
|
69
|
+
setCursorHistory([]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function handleNextPage() {
|
|
73
|
+
if (data?.cursor) {
|
|
74
|
+
setCursorHistory((prev) => [...prev, cursor ?? '']);
|
|
75
|
+
setCursor(data.cursor);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function handlePrevPage() {
|
|
80
|
+
setCursorHistory((prev) => {
|
|
81
|
+
const next = [...prev];
|
|
82
|
+
const prevCursor = next.pop();
|
|
83
|
+
setCursor(prevCursor && prevCursor.length > 0 ? prevCursor : undefined);
|
|
84
|
+
return next;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleDelete() {
|
|
89
|
+
if (!deletingKey) return;
|
|
90
|
+
deleteMutation.mutate(deletingKey, {
|
|
91
|
+
onSuccess: () => setDeletingKey(null),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isLoading) {
|
|
96
|
+
return (
|
|
97
|
+
<div className="flex flex-col gap-4 px-5 py-5">
|
|
98
|
+
<div className="flex items-center justify-center h-[200px]">
|
|
99
|
+
<Spinner size="lg" />
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (error) {
|
|
106
|
+
return (
|
|
107
|
+
<div className="flex flex-col gap-4 px-5 py-5">
|
|
108
|
+
<div className="flex items-center justify-center h-[200px]">
|
|
109
|
+
<p className="text-danger font-mono text-[11px]">
|
|
110
|
+
Failed to load storage items: {error.message}
|
|
111
|
+
</p>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const items = data?.items ?? [];
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div className="flex flex-col gap-4 px-5 py-5">
|
|
121
|
+
<SectionCard
|
|
122
|
+
title="Storage"
|
|
123
|
+
actions={
|
|
124
|
+
<div className="flex items-center gap-2">
|
|
125
|
+
<Input
|
|
126
|
+
placeholder="Prefix filter..."
|
|
127
|
+
value={prefixInput}
|
|
128
|
+
onChange={(e) => setPrefixInput(e.target.value)}
|
|
129
|
+
onKeyDown={(e) => {
|
|
130
|
+
if (e.key === 'Enter') handleFilter();
|
|
131
|
+
}}
|
|
132
|
+
className="h-7 w-48 text-xs"
|
|
133
|
+
/>
|
|
134
|
+
<Button variant="default" size="sm" onClick={handleFilter}>
|
|
135
|
+
Filter
|
|
136
|
+
</Button>
|
|
137
|
+
{activePrefix && (
|
|
138
|
+
<Button variant="ghost" size="sm" onClick={handleClearFilter}>
|
|
139
|
+
Clear
|
|
140
|
+
</Button>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
}
|
|
144
|
+
>
|
|
145
|
+
{items.length === 0 ? (
|
|
146
|
+
<EmptyState
|
|
147
|
+
message={
|
|
148
|
+
activePrefix
|
|
149
|
+
? `No storage items matching prefix "${activePrefix}".`
|
|
150
|
+
: 'No storage items found.'
|
|
151
|
+
}
|
|
152
|
+
/>
|
|
153
|
+
) : (
|
|
154
|
+
<>
|
|
155
|
+
<div className="overflow-x-auto">
|
|
156
|
+
<Table>
|
|
157
|
+
<TableHeader>
|
|
158
|
+
<TableRow>
|
|
159
|
+
<TableHead>Key</TableHead>
|
|
160
|
+
<TableHead>Size</TableHead>
|
|
161
|
+
<TableHead>Type</TableHead>
|
|
162
|
+
<TableHead>Uploaded</TableHead>
|
|
163
|
+
<TableHead>Actions</TableHead>
|
|
164
|
+
</TableRow>
|
|
165
|
+
</TableHeader>
|
|
166
|
+
<TableBody>
|
|
167
|
+
{items.map((blob) => (
|
|
168
|
+
<TableRow key={blob.key}>
|
|
169
|
+
<TableCell
|
|
170
|
+
className="font-mono text-xs max-w-[320px]"
|
|
171
|
+
title={blob.key}
|
|
172
|
+
>
|
|
173
|
+
{blob.key}
|
|
174
|
+
</TableCell>
|
|
175
|
+
<TableCell className="font-mono text-xs text-neutral-400 whitespace-nowrap">
|
|
176
|
+
{formatFileSize(blob.size)}
|
|
177
|
+
</TableCell>
|
|
178
|
+
<TableCell>
|
|
179
|
+
{blob.httpMetadata?.contentType ? (
|
|
180
|
+
<Badge variant="ghost">
|
|
181
|
+
{blob.httpMetadata.contentType}
|
|
182
|
+
</Badge>
|
|
183
|
+
) : (
|
|
184
|
+
<span className="text-neutral-500 text-xs">--</span>
|
|
185
|
+
)}
|
|
186
|
+
</TableCell>
|
|
187
|
+
<TableCell className="whitespace-nowrap text-xs text-neutral-400">
|
|
188
|
+
{formatDateTime(blob.uploaded)}
|
|
189
|
+
</TableCell>
|
|
190
|
+
<TableCell>
|
|
191
|
+
<div className="flex items-center gap-1">
|
|
192
|
+
<Button
|
|
193
|
+
variant="ghost"
|
|
194
|
+
size="sm"
|
|
195
|
+
onClick={() => void download(blob.key)}
|
|
196
|
+
>
|
|
197
|
+
Download
|
|
198
|
+
</Button>
|
|
199
|
+
<Button
|
|
200
|
+
variant="ghost"
|
|
201
|
+
size="sm"
|
|
202
|
+
onClick={() => setDeletingKey(blob.key)}
|
|
203
|
+
>
|
|
204
|
+
Delete
|
|
205
|
+
</Button>
|
|
206
|
+
</div>
|
|
207
|
+
</TableCell>
|
|
208
|
+
</TableRow>
|
|
209
|
+
))}
|
|
210
|
+
</TableBody>
|
|
211
|
+
</Table>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
{/* Pagination */}
|
|
215
|
+
<div className="flex items-center justify-between px-2 py-2">
|
|
216
|
+
<Button
|
|
217
|
+
variant="ghost"
|
|
218
|
+
size="sm"
|
|
219
|
+
disabled={cursorHistory.length === 0}
|
|
220
|
+
onClick={handlePrevPage}
|
|
221
|
+
>
|
|
222
|
+
Previous
|
|
223
|
+
</Button>
|
|
224
|
+
<Button
|
|
225
|
+
variant="ghost"
|
|
226
|
+
size="sm"
|
|
227
|
+
disabled={!data?.truncated}
|
|
228
|
+
onClick={handleNextPage}
|
|
229
|
+
>
|
|
230
|
+
Next
|
|
231
|
+
</Button>
|
|
232
|
+
</div>
|
|
233
|
+
</>
|
|
234
|
+
)}
|
|
235
|
+
</SectionCard>
|
|
236
|
+
|
|
237
|
+
{/* Delete confirmation dialog */}
|
|
238
|
+
<Dialog
|
|
239
|
+
open={deletingKey !== null}
|
|
240
|
+
onOpenChange={() => setDeletingKey(null)}
|
|
241
|
+
>
|
|
242
|
+
<DialogContent>
|
|
243
|
+
<DialogHeader>
|
|
244
|
+
<DialogTitle>Delete Storage Item</DialogTitle>
|
|
245
|
+
</DialogHeader>
|
|
246
|
+
<div className="px-5 py-4 flex flex-col gap-4">
|
|
247
|
+
<p className="font-mono text-[11px] text-neutral-300">
|
|
248
|
+
Are you sure you want to delete{' '}
|
|
249
|
+
<span className="font-mono text-white">{deletingKey}</span>?
|
|
250
|
+
</p>
|
|
251
|
+
<p className="font-mono text-[10px] text-neutral-500">
|
|
252
|
+
This action cannot be undone.
|
|
253
|
+
</p>
|
|
254
|
+
</div>
|
|
255
|
+
<DialogFooter>
|
|
256
|
+
<Button variant="default" onClick={() => setDeletingKey(null)}>
|
|
257
|
+
Cancel
|
|
258
|
+
</Button>
|
|
259
|
+
<Button
|
|
260
|
+
variant="destructive"
|
|
261
|
+
onClick={handleDelete}
|
|
262
|
+
disabled={deleteMutation.isPending}
|
|
263
|
+
>
|
|
264
|
+
{deleteMutation.isPending ? (
|
|
265
|
+
<>
|
|
266
|
+
<Spinner size="sm" /> Deleting...
|
|
267
|
+
</>
|
|
268
|
+
) : (
|
|
269
|
+
'Delete'
|
|
270
|
+
)}
|
|
271
|
+
</Button>
|
|
272
|
+
</DialogFooter>
|
|
273
|
+
</DialogContent>
|
|
274
|
+
</Dialog>
|
|
275
|
+
</div>
|
|
276
|
+
);
|
|
277
|
+
}
|
package/src/pages/Stream.tsx
CHANGED
|
@@ -164,8 +164,11 @@ export function Stream({ initialSelectedEntryId }: StreamProps = {}) {
|
|
|
164
164
|
const { range, setRange } = useTimeRangeState();
|
|
165
165
|
const pageSize = preferences.pageSize;
|
|
166
166
|
const refreshIntervalMs = preferences.refreshInterval * 1000;
|
|
167
|
-
const traceUrlTemplate: string | undefined =
|
|
168
|
-
|
|
167
|
+
const traceUrlTemplate: string | undefined = (
|
|
168
|
+
import.meta as ImportMeta & {
|
|
169
|
+
env?: { VITE_CONSOLE_TRACE_URL_TEMPLATE?: string };
|
|
170
|
+
}
|
|
171
|
+
).env?.VITE_CONSOLE_TRACE_URL_TEMPLATE;
|
|
169
172
|
|
|
170
173
|
const [viewMode, setViewMode] = useState<ViewMode>(() => {
|
|
171
174
|
if (initialSelectedEntryId?.startsWith('#')) return 'commits';
|
|
@@ -268,7 +271,7 @@ export function Stream({ initialSelectedEntryId }: StreamProps = {}) {
|
|
|
268
271
|
selectedEvent?.traceId ?? null,
|
|
269
272
|
selectedEvent?.spanId ?? null
|
|
270
273
|
),
|
|
271
|
-
[selectedEvent?.spanId, selectedEvent?.traceId]
|
|
274
|
+
[selectedEvent?.spanId, selectedEvent?.traceId, traceUrlTemplate]
|
|
272
275
|
);
|
|
273
276
|
|
|
274
277
|
useEffect(() => {
|
package/src/pages/index.ts
CHANGED
package/src/routeTree.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Route as indexRoute } from './routes/index';
|
|
|
5
5
|
import { Route as investigateCommitRoute } from './routes/investigate-commit';
|
|
6
6
|
import { Route as investigateEventRoute } from './routes/investigate-event';
|
|
7
7
|
import { Route as opsRoute } from './routes/ops';
|
|
8
|
+
import { Route as storageRoute } from './routes/storage';
|
|
8
9
|
import { Route as streamRoute } from './routes/stream';
|
|
9
10
|
|
|
10
11
|
export const routeTree = rootRoute.addChildren([
|
|
@@ -14,5 +15,6 @@ export const routeTree = rootRoute.addChildren([
|
|
|
14
15
|
investigateEventRoute,
|
|
15
16
|
fleetRoute,
|
|
16
17
|
opsRoute,
|
|
18
|
+
storageRoute,
|
|
17
19
|
configRoute,
|
|
18
20
|
]);
|
package/src/static-server.ts
CHANGED
|
@@ -125,7 +125,7 @@ function renderIndexHtml(args: {
|
|
|
125
125
|
const resolvedServerUrl = args.prefill?.serverUrl ?? '';
|
|
126
126
|
const resolvedToken = args.prefill?.token ?? '';
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
const withMeta = withMetaTag(
|
|
129
129
|
withMetaTag(
|
|
130
130
|
withMetaTag(args.template, CONSOLE_BASEPATH_META, resolvedBasePath),
|
|
131
131
|
CONSOLE_SERVER_URL_META,
|
|
@@ -134,6 +134,17 @@ function renderIndexHtml(args: {
|
|
|
134
134
|
CONSOLE_TOKEN_META,
|
|
135
135
|
resolvedToken
|
|
136
136
|
);
|
|
137
|
+
|
|
138
|
+
if (resolvedBasePath === '/') {
|
|
139
|
+
return withMeta;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const mountPrefix = resolvedBasePath.replace(/\/+$/g, '');
|
|
143
|
+
return withMeta.replace(
|
|
144
|
+
/(src|href)=("|')\/assets\/([^"']+)("|')/g,
|
|
145
|
+
(_match, attribute, openQuote, assetPath, closeQuote) =>
|
|
146
|
+
`${attribute}=${openQuote}${mountPrefix}/assets/${assetPath}${closeQuote}`
|
|
147
|
+
);
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
export function createConsoleStaticResponder(
|