1<!doctype html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>Upload journal · Neon + Alchemy</title>
7 </head>
8 <body>
9 <main>
10 <header>
11 <span class="eyebrow">NEON + ALCHEMY</span>
12 <h1>Upload journal</h1>
13 <p>Private files. A real upload event. A durable processing record.</p>
14 </header>
15 <p id="message" role="status" aria-live="polite">
16 Connecting to managed authentication…
17 </p>
18 <section aria-labelledby="account-heading">
19 <h2 id="account-heading">Your account</h2>
20 <p id="identity">Signed out</p>
21 <form id="account">
22 <label
23 >Name<input
24 name="name"
25 autocomplete="name"
26 value="Neon explorer"
27 maxlength="100"
28 /></label>
29 <label
30 >Email<input
31 name="email"
32 type="email"
33 autocomplete="username"
34 required
35 /></label>
36 <label
37 >Password<input
38 name="password"
39 type="password"
40 autocomplete="current-password"
41 minlength="8"
42 required
43 /></label>
44 <div class="actions">
45 <button name="mode" value="signup">Create account</button
46 ><button name="mode" value="signin" class="secondary">
47 Sign in
48 </button>
49 </div>
50 </form>
51 <button id="signout" class="secondary" hidden>Sign out</button>
52 </section>
53 <section aria-labelledby="upload-heading">
54 <h2 id="upload-heading">Add a file</h2>
55 <p>
56 Choose a file up to 10 MiB. Your browser uploads directly to a private
57 bucket using a short-lived signed URL.
58 </p>
59 <form id="upload">
60 <label>File<input name="file" type="file" required /></label
61 ><button id="upload-button" disabled>Upload file</button>
62 </form>
63 </section>
64 <section aria-labelledby="files-heading">
65 <div class="section-title">
66 <h2 id="files-heading">Your uploads</h2>
67 <button id="refresh" class="secondary">Refresh status</button>
68 </div>
69 <p id="empty">Sign in to see your files.</p>
70 <ul id="files"></ul>
71 </section>
72 <details>
73 <summary>Security and processing</summary>
74 <p>
75 Only the signed-in owner can request upload or download URLs. The
76 Function verifies JWT signatures and expiry. A bucket event verifies
77 file metadata and updates Postgres; the browser never marks a file
78 ready.
79 </p>
80 <p>
81 Signout ends your session. Previously issued JWTs can remain valid
82 until their short expiry. This tutorial disables email verification
83 and allows localhost; enable verification and restrict origins before
84 production.
85 </p>
86 <p>
87 Size is checked before signing and again after delivery, not enforced
88 as a storage quota by the signed PUT. This is metadata processing, not
89 malware scanning. No AI calls or credits are required.
90 </p>
91 </details>
92 </main>
93 <script type="module" src="/src/main.ts"></script>
94 </body>
95</html>