Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

b7321ca2cd80899250589b9aa08bc3cae9c7cea276282561194e7fc537259b46.png

Forl this val to install:

Install

It uses basic authentication with your Val Town API Token as the password (leave the username field blank).

TODO

  • handle non-textual blobs properly
  • upload a blob by dragging it in (ondrop dropzone on the whole homepage)
  • add upload/download buttons
  • merge edit and view pages
  • add client side navigation using htmx
  • use codemirror instead of a textarea for editing text blobs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/** @jsxImportSource https://esm.sh/hono@4.0.8/jsx **/
import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50";
import view_route from "https://esm.town/v/pomdtr/blob_admin_blob";
import create_route from "https://esm.town/v/pomdtr/blob_admin_create";
import delete_route from "https://esm.town/v/pomdtr/blob_admin_delete";
import edit_route from "https://esm.town/v/pomdtr/blob_admin_edit";
import { passwordAuth } from "https://esm.town/v/pomdtr/password_auth?v=74";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { Hono } from "npm:hono@4.0.8";
import { jsxRenderer } from "npm:hono@4.0.8/jsx-renderer";
const app = new Hono();
app.use(
jsxRenderer(({ children }) => {
return (
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
<title>Blob Admin</title>
</head>
<body>
<main class="container">
{children}
</main>
</body>
</html>
);
}),
);
app.get("/", async (c) => {
let blobs = await blob.list();
return c.render(
<div class="overflow-auto">
<h1>Blob Admin</h1>
<a href="/create" style={{ marginBottom: "1em", display: "inline-block" }}>New Blob</a>
<div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Size (kb)</th>
<th>Last Modified</th>
<th>Edit</th>
<th>Delete</th>
<th>Download</th>
</tr>
</thead>
{blobs.map(b => (
<tr>
<td>
<a href={`/view/${encodeURIComponent(b.key)}`}>
{b.key}
</a>
</td>
<td>{b.size / 1000}</td>
<td>{new Date(b.lastModified).toLocaleString()}</td>
<td>
<a href={`/edit/${encodeURIComponent(b.key)}`}>✍️</a>
</td>
<td>
<a href={`/delete/${encodeURIComponent(b.key)}`}>🗑️</a>
</td>
<td>
<a href={`/download/${encodeURIComponent(b.key)}`}>💾</a>
</td>
</tr>
))}
</table>
</div>
</div>,
);
});
app.route("/create", create_route);
app.route("/view", view_route);
app.route("/edit", edit_route);
app.route("/delete", delete_route);
app.get("/download/:key", (c) => {
return blob.get(c.req.param("key"));
});
export default modifyFetchHandler(passwordAuth(app.fetch));

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

b7321ca2cd80899250589b9aa08bc3cae9c7cea276282561194e7fc537259b46.png

Use this button to install the val:

It uses basic authentication with your Val Town API Token as the password (leave the username field blank).

TODO

  • /new - render a page to write a new blob key and value
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
  • /delete/:blob - delete a blob and render success
  • handle non-textual val properly
  • add upload/download buttons
  • merge edit and view pages
  • add client side navigation using htmx
  • use codemirror instead of a textarea for editing text blobs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/** @jsxImportSource https://esm.sh/hono@4.0.8/jsx **/
import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50";
import view_route from "https://esm.town/v/pomdtr/blob_admin_blob";
import create_route from "https://esm.town/v/pomdtr/blob_admin_create";
import delete_route from "https://esm.town/v/pomdtr/blob_admin_delete";
import edit_route from "https://esm.town/v/pomdtr/blob_admin_edit";
import { passwordAuth } from "https://esm.town/v/pomdtr/password_auth?v=74";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { Hono } from "npm:hono@4.0.8";
import { jsxRenderer } from "npm:hono@4.0.8/jsx-renderer";
const app = new Hono();
app.use(
jsxRenderer(({ children }) => {
return (
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
<title>Blob Admin</title>
</head>
<body>
<main class="container">
{children}
</main>
</body>
</html>
);
}),
);
app.get("/", async (c) => {
let blobs = await blob.list();
return c.render(
<div class="overflow-auto">
<h1>Blob Admin</h1>
<a href="/create" style={{ marginBottom: "1em", display: "inline-block" }}>New Blob</a>
<div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Size (kb)</th>
<th>Last Modified</th>
<th>Edit</th>
<th>Delete</th>
<th>Download</th>
</tr>
</thead>
{blobs.map(b => (
<tr>
<td>
<a href={`/view/${encodeURIComponent(b.key)}`}>
{b.key}
</a>
</td>
<td>{b.size / 1000}</td>
<td>{new Date(b.lastModified).toLocaleString()}</td>
<td>
<a href={`/edit/${encodeURIComponent(b.key)}`}>✍️</a>
</td>
<td>
<a href={`/delete/${encodeURIComponent(b.key)}`}>🗑️</a>
</td>
<td>
<a href={`/download/${encodeURIComponent(b.key)}`}>💾</a>
</td>
</tr>
))}
</table>
</div>
</div>,
);
});
app.route("/create", create_route);
app.route("/view", view_route);
app.route("/edit", edit_route);
app.route("/delete", delete_route);
app.get("/download/:key", (c) => {
return blob.get(c.req.param("key"));
});
export default modifyFetchHandler(passwordAuth(app.fetch));

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

b7321ca2cd80899250589b9aa08bc3cae9c7cea276282561194e7fc537259b46.png

Use this button to install the val:

It uses basic authentication with your Val Town API Token as the password (leave the username field blank).

TODO

  • /new - render a page to write a new blob key and value
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
  • /delete/:blob - delete a blob and render success
  • add upload/download buttons
  • Use modals for create/upload/edit/view/delete page (htmx ?)
  • handle non-textual blobs properly
  • use codemirror instead of a textarea for editing text blobs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono@4.0.8/jsx **/
import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50";
import view_route from "https://esm.town/v/pomdtr/blob_admin_blob";
import create_route from "https://esm.town/v/pomdtr/blob_admin_create";
import delete_route from "https://esm.town/v/pomdtr/blob_admin_delete";
import edit_route from "https://esm.town/v/pomdtr/blob_admin_edit";
import upload_route from "https://esm.town/v/pomdtr/blob_admin_upload";
import { passwordAuth } from "https://esm.town/v/pomdtr/password_auth?v=74";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { Hono } from "npm:hono@4.0.8";
import { jsxRenderer } from "npm:hono@4.0.8/jsx-renderer";
const app = new Hono();
app.use(
jsxRenderer(({ children }) => {
return (
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/code-mirror-web-component@0.0.8/dist/code-mirror.js"
>
</script>
<title>Blob Admin</title>
</head>
<body>
<main class="container">
{children}
</main>
</body>
</html>
);
}),
);
app.get("/", async (c) => {
let blobs = await blob.list();
return c.render(
<div class="overflow-auto">
<h1>Blob Admin</h1>
<section
style={{
display: "flex",
gap: "0.5em",
}}
>
<a href="/create">New Blob</a>
<a href="/upload">Upload Blob</a>
</section>
<section>
<table>
<thead>
<tr>
<th>Name</th>
<th>Size (kb)</th>
<th>Last Modified</th>
<th
style={{
textAlign: "center",
}}
>
Edit
</th>
<th
style={{
textAlign: "center",
}}
>
Delete
</th>
<th
style={{
textAlign: "center",
}}
>
Download
</th>
</tr>
</thead>
{blobs.map(b => (
<tr>
<td>
<a href={`/view/${encodeURIComponent(b.key)}`}>
{b.key}
</a>
</td>
<td>{b.size / 1000}</td>
<td>{new Date(b.lastModified).toLocaleString()}</td>
<td
style={{
textAlign: "center",
}}
>

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

Screenshot 2023-12-13 at 12.51.53.png

To use it on your own Val Town Blob Storage, fork it to your account.

It uses basic authentication with your Val Town API Token as the password (leave the username field blank).

TODO

  • /new - render a page to write a new blob key and value
    • or new blob by file upload
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
    • json validation when the existing content is json
      • checkbox to disable that
  • /delete/:blob - delete a blob and render success
1
2
3
4
5
6
7
8
9
10
11
12
13
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth";
import { blob_admin_blob } from "https://esm.town/v/stevekrouse/blob_admin_blob";
import { blob_admin_home } from "https://esm.town/v/stevekrouse/blob_admin_home";
import { html } from "https://esm.town/v/stevekrouse/html";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await blob_admin_home()));
app.get("/new", (c) => c.html("TODO: New Blob"));
app.get("/:blob", async (c) => c.html(await blob_admin_blob(c.req.param("blob"))));
app.get("/edit/:blob", async (c) => c.html(`TODO: Edit ${c.req.param("blob")}`));
app.get("/delete/:blob", async (c) => c.html(`TODO: Delete ${c.req.param("blob")}`));
export default basicAuth(app.fetch);

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

Screenshot 2023-12-13 at 12.51.53.png

To use it on your own Val Town Blob Storage, fork it to your account.

It uses basic authentication with your Val Town API Token as the password (leave the username field blank).

TODO

  • /new - render a page to write a new blob key and value
    • or new blob by file upload
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
    • json validation when the existing content is json
      • checkbox to disable that
  • /delete/:blob - delete a blob and render success
1
2
3
4
5
6
7
8
9
10
11
12
13
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth";
import { blob_admin_blob } from "https://esm.town/v/stevekrouse/blob_admin_blob";
import { blob_admin_home } from "https://esm.town/v/stevekrouse/blob_admin_home";
import { html } from "https://esm.town/v/stevekrouse/html";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await blob_admin_home()));
app.get("/new", (c) => c.html("TODO: New Blob"));
app.get("/:blob", async (c) => c.html(await blob_admin_blob(c.req.param("blob"))));
app.get("/edit/:blob", async (c) => c.html(`TODO: Edit ${c.req.param("blob")}`));
app.get("/delete/:blob", async (c) => c.html(`TODO: Delete ${c.req.param("blob")}`));
export default basicAuth(app.fetch);

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

Screenshot 2023-12-13 at 12.51.53.png

To use it on your own Val Town Blob Storage, fork it to your account.

It uses basic authentication with your Val Town API Token as the password (leave the username field blank).

TODO

  • /new - render a page to write a new blob key and value
    • or new blob by file upload
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
    • json validation when the existing content is json
      • checkbox to disable that
  • /delete/:blob - delete a blob and render success
1
2
3
4
5
6
7
8
9
10
11
12
13
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth?v=38";
import { blob_admin_blob } from "https://esm.town/v/stevekrouse/blob_admin_blob";
import { blob_admin_home } from "https://esm.town/v/stevekrouse/blob_admin_home";
import { html } from "https://esm.town/v/stevekrouse/html";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await blob_admin_home()));
app.get("/:blob", async (c) => c.html(await blob_admin_blob(c.req.param("blob"))));
app.get("/new", (c) => c.html("TODO: New Blob"));
app.get("/edit/:blob", async (c) => c.html(`TODO: Edit ${c.req.param("blob")}`));
app.get("/delete/:blob", async (c) => c.html(`TODO: Delete ${c.req.param("blob")}`));
export default basicAuth(app.fetch);
1
Next