storage-explorer 0.1.0
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/README.md +54 -0
- package/dist/chunk-js4y3bna.css +442 -0
- package/dist/chunk-vtsn1g38.js +1022 -0
- package/dist/index-3xfxtfws.js +238 -0
- package/dist/index-3xfxtfws.js.map +24 -0
- package/dist/index-67w6q0ny.css +1 -0
- package/dist/index-9t8tyk25.js +238 -0
- package/dist/index-9t8tyk25.js.map +24 -0
- package/dist/index-b7b12360.css +1 -0
- package/dist/index-bz8f0q85.js +238 -0
- package/dist/index-bz8f0q85.js.map +18 -0
- package/dist/index-vw9287sb.js +238 -0
- package/dist/index-vw9287sb.js.map +18 -0
- package/dist/index-xde44bqw.css +1 -0
- package/dist/index.html +13 -0
- package/dist/index.js +29485 -0
- package/dist/logo-kygw735p.svg +1 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# s3-explorer
|
|
2
|
+
|
|
3
|
+
Read-only AWS S3 / S3-compatible explorer built with Bun + React.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Run in development
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Build
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bun run build
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What it does
|
|
24
|
+
|
|
25
|
+
- Save multiple S3 connection profiles in browser `localStorage`
|
|
26
|
+
- Test a profile connection
|
|
27
|
+
- List buckets
|
|
28
|
+
- Open a known bucket manually (for keys without ListBuckets permission)
|
|
29
|
+
- Browse folders and files inside a bucket using prefix navigation
|
|
30
|
+
- Load next pages of objects (default page size: `200`)
|
|
31
|
+
|
|
32
|
+
## Profile fields
|
|
33
|
+
|
|
34
|
+
- Endpoint URL (required)
|
|
35
|
+
- Access Key ID (required)
|
|
36
|
+
- Secret Access Key (required)
|
|
37
|
+
- Region (optional, defaults to `us-east-1`)
|
|
38
|
+
- Force path style (enabled by default for better S3-compatible support)
|
|
39
|
+
|
|
40
|
+
## Security note
|
|
41
|
+
|
|
42
|
+
Credentials are stored in browser `localStorage` on your machine.
|
|
43
|
+
This is convenient, but less secure than a dedicated secrets manager.
|
|
44
|
+
Treat this app as a local/internal tool and avoid using it on untrusted shared browsers.
|
|
45
|
+
|
|
46
|
+
## API routes
|
|
47
|
+
|
|
48
|
+
The frontend calls Bun server endpoints, and the server talks to S3:
|
|
49
|
+
|
|
50
|
+
- `POST /api/s3/test-connection`
|
|
51
|
+
- `POST /api/s3/list-buckets`
|
|
52
|
+
- `POST /api/s3/list-objects`
|
|
53
|
+
|
|
54
|
+
All routes are read-only.
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/* src/index.css */
|
|
2
|
+
:root {
|
|
3
|
+
color: #152234;
|
|
4
|
+
--bg-top: #f9f3ea;
|
|
5
|
+
--bg-bottom: #e8eff8;
|
|
6
|
+
--panel: #ffffffd8;
|
|
7
|
+
--panel-border: #cdd9e8;
|
|
8
|
+
--text: #152234;
|
|
9
|
+
--muted: #5d6f84;
|
|
10
|
+
--accent: #146c94;
|
|
11
|
+
--accent-soft: #e1f0f8;
|
|
12
|
+
--danger: #b9414f;
|
|
13
|
+
--ok: #1f8b4c;
|
|
14
|
+
--radius: 14px;
|
|
15
|
+
background: #f6f8fb;
|
|
16
|
+
font-family: Segoe UI, Avenir Next, Helvetica Neue, sans-serif;
|
|
17
|
+
font-weight: 400;
|
|
18
|
+
line-height: 1.4;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
* {
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
color: var(--text);
|
|
27
|
+
background: linear-gradient(168deg, var(--bg-top), var(--bg-bottom));
|
|
28
|
+
min-width: 320px;
|
|
29
|
+
min-height: 100vh;
|
|
30
|
+
margin: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
body:before {
|
|
34
|
+
content: "";
|
|
35
|
+
position: fixed;
|
|
36
|
+
z-index: -1;
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
background: radial-gradient(circle at 20% 18%, #fffa 0, #0000 36%), radial-gradient(circle at 85% 12%, #d9e9fa88 0, #0000 28%), radial-gradient(circle at 70% 80%, #f8e8d6aa 0, #0000 35%);
|
|
39
|
+
inset: 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#root {
|
|
43
|
+
width: 100%;
|
|
44
|
+
min-height: 100vh;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.app-shell {
|
|
48
|
+
display: grid;
|
|
49
|
+
grid-template-columns: 360px minmax(0, 1fr);
|
|
50
|
+
gap: 1rem;
|
|
51
|
+
width: min(1320px, 100% - 2rem);
|
|
52
|
+
margin: 1rem auto;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.main-column {
|
|
56
|
+
display: grid;
|
|
57
|
+
align-content: start;
|
|
58
|
+
gap: 1rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.panel {
|
|
62
|
+
background: var(--panel);
|
|
63
|
+
backdrop-filter: blur(10px);
|
|
64
|
+
border: 1px solid var(--panel-border);
|
|
65
|
+
border-radius: var(--radius);
|
|
66
|
+
box-shadow: 0 10px 24px #12233712;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.panel-step {
|
|
70
|
+
display: inline-block;
|
|
71
|
+
color: var(--accent);
|
|
72
|
+
background: var(--accent-soft);
|
|
73
|
+
border-radius: 999px;
|
|
74
|
+
margin-bottom: .6rem;
|
|
75
|
+
padding: .2rem .55rem;
|
|
76
|
+
font-size: .73rem;
|
|
77
|
+
font-weight: 600;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.profile-panel {
|
|
81
|
+
position: sticky;
|
|
82
|
+
overflow: auto;
|
|
83
|
+
max-height: calc(100vh - 2rem);
|
|
84
|
+
padding: 1rem;
|
|
85
|
+
top: 1rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.explorer-panel, .object-panel {
|
|
89
|
+
padding: 1rem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.panel-header h1, .explorer-header h2, .objects-header h3 {
|
|
93
|
+
letter-spacing: .01em;
|
|
94
|
+
margin: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.panel-header p, .explorer-header p, .objects-header p {
|
|
98
|
+
color: var(--muted);
|
|
99
|
+
margin: .35rem 0 0;
|
|
100
|
+
font-size: .92rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.profiles-toolbar {
|
|
104
|
+
display: flex;
|
|
105
|
+
justify-content: space-between;
|
|
106
|
+
align-items: center;
|
|
107
|
+
margin-top: 1rem;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.profiles-list {
|
|
111
|
+
display: grid;
|
|
112
|
+
gap: .5rem;
|
|
113
|
+
margin-top: .65rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.profile-card {
|
|
117
|
+
border: 1px solid var(--panel-border);
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
background: #fff;
|
|
120
|
+
border-radius: 10px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.profile-card.active {
|
|
124
|
+
border-color: var(--accent);
|
|
125
|
+
box-shadow: inset 0 0 0 1px var(--accent);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.profile-select {
|
|
129
|
+
text-align: left;
|
|
130
|
+
display: grid;
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
background: none;
|
|
133
|
+
border: 0;
|
|
134
|
+
gap: .25rem;
|
|
135
|
+
width: 100%;
|
|
136
|
+
padding: .65rem .75rem;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.profile-name {
|
|
140
|
+
font-weight: 600;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.profile-endpoint {
|
|
144
|
+
color: var(--muted);
|
|
145
|
+
overflow-wrap: anywhere;
|
|
146
|
+
font-size: .84rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.delete-button {
|
|
150
|
+
border: 0;
|
|
151
|
+
border-top: 1px solid var(--panel-border);
|
|
152
|
+
color: var(--danger);
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
background: #fff;
|
|
155
|
+
width: 100%;
|
|
156
|
+
padding: .45rem .75rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.profile-form {
|
|
160
|
+
display: grid;
|
|
161
|
+
gap: .7rem;
|
|
162
|
+
margin-top: 1rem;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.profile-form label {
|
|
166
|
+
display: grid;
|
|
167
|
+
gap: .35rem;
|
|
168
|
+
font-size: .9rem;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
input, button {
|
|
172
|
+
font: inherit;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.profile-form input, .manual-bucket-row input {
|
|
176
|
+
border: 1px solid var(--panel-border);
|
|
177
|
+
color: var(--text);
|
|
178
|
+
background: #fff;
|
|
179
|
+
border-radius: 9px;
|
|
180
|
+
flex: 1;
|
|
181
|
+
padding: .55rem .65rem;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.profile-form input:focus, .manual-bucket-row input:focus {
|
|
185
|
+
outline: 2px solid #0000;
|
|
186
|
+
border-color: var(--accent);
|
|
187
|
+
box-shadow: 0 0 0 2px #146c9430;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.secret-row {
|
|
191
|
+
display: flex;
|
|
192
|
+
gap: .5rem;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.secret-row input {
|
|
196
|
+
flex: 1;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.checkbox-row {
|
|
200
|
+
grid-template-columns: auto 1fr;
|
|
201
|
+
align-items: center;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.checkbox-row input {
|
|
205
|
+
width: 1rem;
|
|
206
|
+
height: 1rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.form-actions {
|
|
210
|
+
display: flex;
|
|
211
|
+
flex-wrap: wrap;
|
|
212
|
+
gap: .55rem;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.primary-button, .secondary-button, .ghost-button {
|
|
216
|
+
cursor: pointer;
|
|
217
|
+
border-radius: 9px;
|
|
218
|
+
padding: .55rem .85rem;
|
|
219
|
+
transition: all .15s;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.primary-button {
|
|
223
|
+
border: 1px solid var(--accent);
|
|
224
|
+
background: var(--accent);
|
|
225
|
+
color: #fff;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.primary-button:hover {
|
|
229
|
+
filter: brightness(1.05);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.secondary-button {
|
|
233
|
+
border: 1px solid var(--panel-border);
|
|
234
|
+
color: var(--text);
|
|
235
|
+
background: #fff;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.ghost-button {
|
|
239
|
+
border: 1px solid var(--panel-border);
|
|
240
|
+
color: var(--muted);
|
|
241
|
+
background: #fff;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.secondary-button:hover, .ghost-button:hover, .delete-button:hover, .bucket-item:hover, .object-row.folder:hover, .breadcrumb-link:hover {
|
|
245
|
+
background: var(--accent-soft);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
button:disabled {
|
|
249
|
+
opacity: .58;
|
|
250
|
+
cursor: not-allowed;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.status-ok, .status-error, .empty-copy {
|
|
254
|
+
margin: .75rem 0 0;
|
|
255
|
+
font-size: .88rem;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.status-ok {
|
|
259
|
+
color: var(--ok);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.status-error {
|
|
263
|
+
color: var(--danger);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.empty-copy, .muted, .helper-copy {
|
|
267
|
+
color: var(--muted);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.explorer-header, .objects-header {
|
|
271
|
+
display: flex;
|
|
272
|
+
justify-content: space-between;
|
|
273
|
+
align-items: flex-start;
|
|
274
|
+
gap: 1rem;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.manual-bucket-form {
|
|
278
|
+
display: grid;
|
|
279
|
+
border: 1px solid var(--panel-border);
|
|
280
|
+
background: #fff;
|
|
281
|
+
border-radius: 10px;
|
|
282
|
+
gap: .45rem;
|
|
283
|
+
margin-top: .9rem;
|
|
284
|
+
padding: .7rem;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.manual-bucket-form label {
|
|
288
|
+
color: var(--muted);
|
|
289
|
+
font-size: .88rem;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.manual-bucket-row {
|
|
293
|
+
display: flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
gap: .5rem;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.helper-copy {
|
|
299
|
+
margin: 0;
|
|
300
|
+
font-size: .82rem;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.buckets-grid {
|
|
304
|
+
display: grid;
|
|
305
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
306
|
+
gap: .5rem;
|
|
307
|
+
margin-top: .8rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.bucket-item {
|
|
311
|
+
border: 1px solid var(--panel-border);
|
|
312
|
+
text-align: left;
|
|
313
|
+
display: grid;
|
|
314
|
+
cursor: pointer;
|
|
315
|
+
background: #fff;
|
|
316
|
+
border-radius: 10px;
|
|
317
|
+
gap: .2rem;
|
|
318
|
+
padding: .7rem;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.bucket-item.active {
|
|
322
|
+
border-color: var(--accent);
|
|
323
|
+
box-shadow: inset 0 0 0 1px var(--accent);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.bucket-item small {
|
|
327
|
+
color: var(--muted);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.objects-actions {
|
|
331
|
+
display: flex;
|
|
332
|
+
flex-wrap: wrap;
|
|
333
|
+
gap: .5rem;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.path-breadcrumb {
|
|
337
|
+
border: 1px solid var(--panel-border);
|
|
338
|
+
display: flex;
|
|
339
|
+
background: #fff;
|
|
340
|
+
border-radius: 10px;
|
|
341
|
+
flex-wrap: wrap;
|
|
342
|
+
align-items: center;
|
|
343
|
+
gap: .1rem;
|
|
344
|
+
margin-top: .8rem;
|
|
345
|
+
padding: .55rem .7rem;
|
|
346
|
+
font-size: .88rem;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.path-breadcrumb.muted {
|
|
350
|
+
color: var(--muted);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.breadcrumb-link {
|
|
354
|
+
cursor: pointer;
|
|
355
|
+
color: var(--text);
|
|
356
|
+
background: none;
|
|
357
|
+
border: 0;
|
|
358
|
+
border-radius: 6px;
|
|
359
|
+
padding: .2rem .4rem;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.breadcrumb-segment {
|
|
363
|
+
display: inline-flex;
|
|
364
|
+
align-items: center;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.breadcrumb-separator {
|
|
368
|
+
color: var(--muted);
|
|
369
|
+
margin: 0 .05rem;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.objects-list {
|
|
373
|
+
border: 1px solid var(--panel-border);
|
|
374
|
+
overflow: auto;
|
|
375
|
+
background: #fff;
|
|
376
|
+
border-radius: 10px;
|
|
377
|
+
min-height: 320px;
|
|
378
|
+
margin-top: .8rem;
|
|
379
|
+
padding: .5rem;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.object-row {
|
|
383
|
+
color: var(--text);
|
|
384
|
+
display: grid;
|
|
385
|
+
grid-template-columns: minmax(0, 1fr) 140px 210px;
|
|
386
|
+
text-align: left;
|
|
387
|
+
background: #fff;
|
|
388
|
+
border: 0;
|
|
389
|
+
border-bottom: 1px solid #e7edf6;
|
|
390
|
+
align-items: center;
|
|
391
|
+
gap: .8rem;
|
|
392
|
+
width: 100%;
|
|
393
|
+
padding: .65rem;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.object-row:last-child {
|
|
397
|
+
border-bottom: 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.object-row.folder {
|
|
401
|
+
cursor: pointer;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.object-name {
|
|
405
|
+
overflow-wrap: anywhere;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.object-meta {
|
|
409
|
+
color: var(--muted);
|
|
410
|
+
text-align: right;
|
|
411
|
+
white-space: nowrap;
|
|
412
|
+
font-size: .82rem;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
@media (width <= 1020px) {
|
|
416
|
+
.app-shell {
|
|
417
|
+
grid-template-columns: 1fr;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.profile-panel {
|
|
421
|
+
position: static;
|
|
422
|
+
max-height: none;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.explorer-header, .objects-header {
|
|
426
|
+
flex-direction: column;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.manual-bucket-row {
|
|
430
|
+
flex-direction: column;
|
|
431
|
+
align-items: stretch;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.object-row {
|
|
435
|
+
grid-template-columns: 1fr;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.object-meta {
|
|
439
|
+
text-align: left;
|
|
440
|
+
white-space: normal;
|
|
441
|
+
}
|
|
442
|
+
}
|