- Server factory with Fastify plugins (JWT, cookie, multipart, formbody, static) - Auth middleware: requireAuth preHandler (401 for API, redirect for pages) - Auth API routes: POST /api/v1/auth/login, POST /api/v1/auth/logout - File API routes: GET/POST /api/v1/files, DELETE /api/v1/files/:id - Page routes: /, /login, /logout, /upload, /files, /files/:id/delete, /f/:id, /f/:id/raw - HTML views: layout, login, upload, file-list, file-view, not-found - CLI register-user script - public/style.css dark theme - Test helpers: createTestApp, loginAs, buildMultipart - Integration tests for auth API, file API, and page routes (51 tests passing) - Update CLAUDE.md with red/green TDD and commit instructions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
127 lines
2.9 KiB
CSS
127 lines
2.9 KiB
CSS
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
:root {
|
|
--bg: #0f1117;
|
|
--surface: #1c1f2e;
|
|
--border: #2a2d3e;
|
|
--accent: #6c63ff;
|
|
--accent-hover: #5a52e0;
|
|
--text: #e2e8f0;
|
|
--muted: #8892a4;
|
|
--error: #f56565;
|
|
--danger: #e53e3e;
|
|
}
|
|
|
|
body {
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
a { color: var(--accent); text-decoration: none; }
|
|
a:hover { text-decoration: underline; }
|
|
|
|
header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1.5rem;
|
|
padding: 1rem 2rem;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.logo { font-size: 1.25rem; font-weight: 700; color: var(--text); }
|
|
|
|
nav { display: flex; align-items: center; gap: 1rem; margin-left: auto; }
|
|
nav a { color: var(--muted); font-size: 0.9rem; }
|
|
nav a:hover { color: var(--text); }
|
|
|
|
main { flex: 1; padding: 2rem; display: flex; justify-content: center; align-items: flex-start; }
|
|
|
|
.form-container {
|
|
width: 100%;
|
|
max-width: 420px;
|
|
margin-top: 4rem;
|
|
}
|
|
|
|
.form-container h1 { margin-bottom: 1.5rem; font-size: 1.5rem; }
|
|
|
|
form { display: flex; flex-direction: column; gap: 1rem; }
|
|
|
|
label {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem;
|
|
font-size: 0.875rem;
|
|
color: var(--muted);
|
|
}
|
|
|
|
input[type="text"],
|
|
input[type="password"],
|
|
input[type="file"] {
|
|
padding: 0.625rem 0.75rem;
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
color: var(--text);
|
|
font-size: 1rem;
|
|
width: 100%;
|
|
}
|
|
|
|
input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
|
|
|
|
button, .btn {
|
|
padding: 0.625rem 1.25rem;
|
|
background: var(--accent);
|
|
border: none;
|
|
border-radius: 6px;
|
|
color: #fff;
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
display: inline-block;
|
|
}
|
|
|
|
button:hover, .btn:hover { background: var(--accent-hover); }
|
|
|
|
button.danger { background: var(--danger); }
|
|
button.danger:hover { background: #c53030; }
|
|
|
|
.error { color: var(--error); font-size: 0.9rem; margin-bottom: 0.5rem; }
|
|
|
|
.share-box {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin: 1rem 0;
|
|
}
|
|
.share-box input { flex: 1; }
|
|
|
|
/* File view — centered */
|
|
.file-view {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 60vh;
|
|
gap: 1.5rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.file-view h1 { font-size: 1.25rem; color: var(--muted); word-break: break-all; }
|
|
|
|
.file-view video,
|
|
.file-view audio { max-width: 800px; width: 100%; border-radius: 8px; }
|
|
|
|
.file-actions { display: flex; gap: 1rem; }
|
|
|
|
/* File list table */
|
|
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
|
|
th, td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid var(--border); }
|
|
th { color: var(--muted); font-weight: 600; }
|
|
tr:hover td { background: var(--surface); }
|
|
|
|
h1 { margin-bottom: 1rem; }
|