Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions admin-ui/src/test/sw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ describe('service worker', () => {
expect(network).toHaveBeenCalledTimes(1);
});

it("fetches the connect and setup pages' stylesheet and scripts from the network even when cached", async () => {
for (const path of ['/assets/style.css', '/assets/theme-init.js']) {
const { network, request } = loadWorker({ [path]: 'stale' });
await expect(request(path)).resolves.toMatchObject({ body: 'network' });
expect(network).toHaveBeenCalledTimes(1);
}
});

it('serves fonts and icons from the cache first', async () => {
const { request } = loadWorker({ '/assets/fonts/figtree-variable.woff2': 'cached' });
await expect(request('/assets/fonts/figtree-variable.woff2')).resolves.toBe('cached');
});

it('serves hashed console assets from the cache first', async () => {
const { request } = loadWorker({ '/admin/assets/index-abc.js': 'cached' });
await expect(request('/admin/assets/index-abc.js')).resolves.toBe('cached');
Expand Down
13 changes: 9 additions & 4 deletions internal/web/adminui/adminui.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,22 @@ func Handler(fsys fs.FS, csp string) http.Handler {
}

// notBuiltPage is what a source build without the console serves at /admin. It
// is self-contained: no inline style or script, so the strict CSP holds.
// wears the connect page's stylesheet (internal/web/assets) and has no inline style
// or script, so the strict CSP holds.
const notBuiltPage = `<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>AudioSilo - admin console not built</title></head>
<title>AudioSilo - admin console not built</title>
<script src="/assets/theme-init.js"></script>
<link rel="stylesheet" href="/assets/style.css"></head>
<body>
<div class="center"><div class="column"><div class="card">
<h1>The admin console isn't built</h1>
<p>This server was compiled without the admin console. Build it, then rebuild the server:</p>
<p class="muted">This server was compiled without the admin console. Build it, then rebuild the server:</p>
<pre>scripts/build-admin.sh
go build ./cmd/audiosilo</pre>
<p>Release binaries and the Docker image include it.</p>
<p class="muted">Release binaries and the Docker image include it.</p>
</div></div></div>
</body>
</html>
`
Expand Down
Binary file not shown.
Binary file added internal/web/assets/fonts/figtree-variable.woff2
Binary file not shown.
Binary file removed internal/web/assets/fonts/roboto-400.woff2
Binary file not shown.
Binary file removed internal/web/assets/fonts/roboto-500.woff2
Binary file not shown.
Binary file removed internal/web/assets/fonts/roboto-700.woff2
Binary file not shown.
49 changes: 25 additions & 24 deletions internal/web/assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,40 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title data-i18n="connect.docTitle">AudioSilo - Connect</title>
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
<script src="/assets/theme-init.js"></script>
<link rel="stylesheet" href="/assets/style.css">
</head>
<body>
<div class="center">
<div class="card narrow">
<div class="column">
<div class="brand">
<img class="logo" src="/assets/favicon.svg" alt="">
<span class="wordmark"><span class="a">AUDIO</span><span class="s">SILO</span></span>
<span class="tile"><span class="mark"></span></span>
<span class="wordmark">AudioSilo</span>
</div>
<div class="spacer"></div>
<h1 data-i18n="connect.heading">Connect a device</h1>
<p class="muted" data-i18n="connect.intro">Enter the auth code from your server administrator.</p>
<div class="card">
<h1 data-i18n="connect.heading">Connect a device</h1>
<p class="muted" data-i18n="connect.intro">Enter the auth code from your server administrator.</p>

<form id="redeem-form">
<div class="spacer"></div>
<div class="field">
<label for="code" data-i18n="connect.codeLabel">Auth code</label>
<input id="code" name="code" placeholder="XXXX-XXXX-XXXX-XXXX" autocomplete="off" autocapitalize="characters" required>
</div>
<button type="submit" class="block" data-i18n="connect.continue">Continue</button>
</form>
<form id="redeem-form">
<div class="field">
<label for="code" data-i18n="connect.codeLabel">Auth code</label>
<input id="code" name="code" placeholder="XXXX-XXXX-XXXX-XXXX" autocomplete="off" autocapitalize="characters" required>
</div>
<button type="submit" class="block" data-i18n="connect.continue">Continue</button>
</form>

<div id="msg" class="msg error"></div>
<div id="msg" class="msg" role="alert"></div>

<div id="result" class="hidden">
<h2 data-i18n="connect.scanHeading">Scan to pair</h2>
<div class="qr"><img id="qr" alt="Pairing QR code" data-i18n-alt="connect.qrAlt"></div>
<p class="muted" data-i18n="connect.scanHint">Each device you're setting up can scan this code, or continue on this device.</p>
<p id="uses-note" class="muted hidden"></p>
<div class="links">
<a id="link-app" href="#" aria-disabled="true" data-i18n="connect.openApp">Open in app</a>
<a id="link-web" href="#" aria-disabled="true" class="primary" data-i18n="connect.openWeb">Open web player</a>
<a id="link-admin" href="/admin" data-i18n="connect.adminSection">Admin section</a>
<div id="result" class="hidden">
<h2 data-i18n="connect.scanHeading">Scan to pair</h2>
<div class="qr"><img id="qr" alt="Pairing QR code" data-i18n-alt="connect.qrAlt"></div>
<p class="muted" data-i18n="connect.scanHint">Each device you're setting up can scan this code, or continue on this device.</p>
<p id="uses-note" class="muted hidden"></p>
<div class="links">
<a id="link-app" class="button secondary block" href="#" aria-disabled="true" data-i18n="connect.openApp">Open in app</a>
<a id="link-web" class="button block" href="#" aria-disabled="true" data-i18n="connect.openWeb">Open web player</a>
<a id="link-admin" class="button secondary block" href="/admin" data-i18n="connect.adminSection">Admin section</a>
</div>
</div>
</div>

Expand Down
70 changes: 35 additions & 35 deletions internal/web/assets/setup.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,51 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AudioSilo - Setup</title>
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
<script src="/assets/theme-init.js"></script>
<link rel="stylesheet" href="/assets/style.css">
</head>
<body>
<!-- First-run setup wizard. Served only until an admin exists, and POST is
token-guarded. All behaviour lives in /assets/setup.js (strict CSP). -->
<div class="center">
<div class="card narrow">
<div class="column">
<div class="brand">
<img class="logo" src="/assets/favicon.svg" alt="">
<span class="wordmark"><span class="a">AUDIO</span><span class="s">SILO</span></span>
<span class="tile"><span class="mark"></span></span>
<span class="wordmark">AudioSilo</span>
</div>
<div class="spacer"></div>
<h1>Welcome - let's set up your server</h1>
<p class="muted">Create your admin account and point AudioSilo at the folder where your audiobooks live. You can change all of this later in the admin console.</p>
<div class="card">
<h1>Welcome - let's set up your server</h1>
<p class="muted">Create your admin account and point AudioSilo at the folder where your audiobooks live. You can change all of this later in the admin console.</p>

<form id="setup-form">
<div class="spacer"></div>
<div class="field">
<label for="username">Admin username</label>
<input id="username" value="admin" autocomplete="username" required>
</div>
<div class="field">
<label for="password">Admin password</label>
<input id="password" type="password" autocomplete="new-password" required>
<p class="muted">At least 8 characters - you'll sign in with this.</p>
</div>
<div class="field">
<label for="library-name">Library name</label>
<input id="library-name" value="Audiobooks" required>
</div>
<div class="field">
<label for="library-root">Books folder (full path on this computer)</label>
<input id="library-root" placeholder="/Users/you/Audiobooks" required>
</div>
<button type="submit" class="block" id="setup-submit">Finish setup</button>
</form>
<div id="setup-msg" class="msg error"></div>
<form id="setup-form">
<div class="field">
<label for="username">Admin username</label>
<input id="username" value="admin" autocomplete="username" required>
</div>
<div class="field">
<label for="password">Admin password</label>
<input id="password" type="password" autocomplete="new-password" required>
<p class="muted">At least 8 characters - you'll sign in with this.</p>
</div>
<div class="field">
<label for="library-name">Library name</label>
<input id="library-name" value="Audiobooks" required>
</div>
<div class="field">
<label for="library-root">Books folder (full path on this computer)</label>
<input id="library-root" placeholder="/Users/you/Audiobooks" required>
</div>
<button type="submit" class="block" id="setup-submit">Finish setup</button>
</form>
<div id="setup-msg" class="msg" role="alert"></div>

<div id="setup-done" class="hidden">
<div class="spacer"></div>
<h2>You're all set 🎉</h2>
<p class="muted">Your library is scanning now. Open the admin console to manage it, or the web player to start listening.</p>
<div class="inline">
<a href="/admin"><button type="button">Open admin console</button></a>
<a href="/web"><button type="button" class="secondary">Open web player</button></a>
<div id="setup-done" class="hidden">
<h2>You're all set 🎉</h2>
<p class="muted">Your library is scanning now. Open the admin console to manage it, or the web player to start listening.</p>
<div class="inline">
<a class="button" href="/admin">Open admin console</a>
<a class="button secondary" href="/web">Open web player</a>
</div>
</div>
</div>
</div>
Expand Down
Loading
Loading