Skip to content

Commit 728690b

Browse files
Merge pull request #14 from MaddyGuthridge/maddy-password-change-ui
Implement UI for changing auth information
2 parents 061bc58 + 7ddf8aa commit 728690b

File tree

8 files changed

+269
-122
lines changed

8 files changed

+269
-122
lines changed

src/components/navbar/Navbar.svelte

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script lang="ts">
2-
import { dev } from '$app/environment';
3-
import { goto } from '$app/navigation';
4-
import api from '$endpoints';
5-
import type { ConfigJson } from '$lib/server/data/config';
6-
import Separator from '$components/Separator.svelte';
2+
import { dev } from "$app/environment";
3+
import { goto } from "$app/navigation";
4+
import api from "$endpoints";
5+
import type { ConfigJson } from "$lib/server/data/config";
6+
import Separator from "$components/Separator.svelte";
77
8-
export let path: { url: string, txt: string }[];
8+
export let path: { url: string; txt: string }[];
99
export let config: ConfigJson;
1010
/** Whether the user is logged in. Set to undefined if auth is disabled */
1111
export let loggedIn: boolean | undefined;
@@ -16,16 +16,26 @@
1616
location.reload();
1717
}
1818
19+
/**
20+
* Go to the login page, with the from parameter determining the origin page.
21+
*/
22+
async function gotoLogin() {
23+
await goto(`/admin/login?from=${window.location.pathname}`);
24+
}
25+
1926
/** Clear all data, and take the user to the firstrun page */
2027
async function clear() {
2128
await api().debug.clear();
22-
await goto('/admin/firstrun');
29+
await goto("/admin/firstrun");
2330
}
2431
2532
// This function needs to accept `path` as an input, otherwise the links
2633
// stop being reactive due to cacheing or something
27-
function pathTo(path: { url: string, txt: string }[], i: number) {
28-
return path.slice(0, i + 1).map(p => p.url).join('/');
34+
function pathTo(path: { url: string; txt: string }[], i: number) {
35+
return path
36+
.slice(0, i + 1)
37+
.map((p) => p.url)
38+
.join("/");
2939
}
3040
</script>
3141

@@ -38,7 +48,7 @@
3848
<a href="/">{config.siteShortName}</a> /
3949
{#each path.slice(0, -1) as p, i}
4050
<a href="/{pathTo(path, i)}">{p.txt}</a>
41-
{'/ '}
51+
{"/ "}
4252
{/each}
4353
{path[path.length - 1].txt}
4454
</h1>
@@ -48,14 +58,14 @@
4858
<!-- Control buttons -->
4959
<span id="control-buttons">
5060
{#if loggedIn}
51-
<button on:click={() => goto('/admin')}> Admin </button>
61+
<button on:click={() => goto("/admin")}> Admin </button>
5262
<button on:click={logOut}> Log out </button>
5363
{:else if loggedIn !== undefined}
5464
<!-- Only include a login button if logging in is enabled -->
55-
<button on:click={() => goto('/admin/login')}> Log in </button>
65+
<button on:click={gotoLogin}> Log in </button>
5666
{/if}
5767
<!-- About button navigates to about page -->
58-
<button on:click={() => goto('/about')}> About </button>
68+
<button on:click={() => goto("/about")}> About </button>
5969
<!-- In dev mode, add a quick shortcut to delete everything -->
6070
{#if dev}
6171
<Separator />

src/routes/admin/+page.svelte

Lines changed: 18 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,26 @@
11
<script lang="ts">
2-
import api from '$endpoints';
3-
import Background from '$components/Background.svelte';
4-
import Navbar from '$components/navbar';
5-
import Paper from '$components/Paper.svelte';
6-
import consts from '$lib/consts';
7-
8-
export let data: import('./$types').PageData;
9-
10-
// Git setup
11-
let gitUrl = '';
12-
13-
async function submitSwitchToGit() {
14-
await api().admin.git.init(gitUrl);
15-
}
16-
17-
// Git controls
18-
let commitMessage = '';
19-
20-
async function gitCommit() {
21-
await api().admin.git.commit(commitMessage);
22-
}
2+
import Background from "$components/Background.svelte";
3+
import Navbar from "$components/navbar";
4+
import Paper from "$components/Paper.svelte";
5+
import consts from "$lib/consts";
6+
import GitSettings from "./GitSettings.svelte";
7+
import ChangePassword from "./ChangePassword.svelte";
8+
import ReloadData from "./ReloadData.svelte";
9+
import LogOutAll from "./LogOutAll.svelte";
10+
11+
export let data: import("./$types").PageData;
2312
</script>
2413

2514
<svelte:head>
2615
<title>Admin - {data.globals.config.siteName}</title>
27-
<meta name="generator" content="{consts.APP_NAME}">
28-
<meta name="theme-color" content="{data.globals.config.color}">
16+
<meta name="generator" content={consts.APP_NAME} />
17+
<meta name="theme-color" content={data.globals.config.color} />
2918
<!-- Prevent web crawlers from indexing the admin page -->
30-
<meta name="robots" content="noindex">
19+
<meta name="robots" content="noindex" />
3120
</svelte:head>
3221

3322
<Navbar
34-
path={[{ txt: 'Admin', url: 'admin' }]}
23+
path={[{ txt: "Admin", url: "admin" }]}
3524
config={data.globals.config}
3625
loggedIn={true}
3726
/>
@@ -42,70 +31,10 @@
4231
<div id="paper-container">
4332
<Paper>
4433
<div id="contents">
45-
<div>
46-
{#if data.repo}
47-
<h2>Git status</h2>
48-
<p>Current branch: {data.repo.branch}</p>
49-
<p>Current commit: {data.repo.commit}</p>
50-
<p>
51-
{#if data.repo.behind}
52-
{data.repo.behind} commits behind.
53-
{/if}
54-
{#if data.repo.ahead}
55-
{data.repo.ahead} commits ahead.
56-
{/if}
57-
</p>
58-
59-
<!-- Push/pull -->
60-
{#if data.repo.behind}
61-
<button on:click={() => api().admin.git.push()}>Push</button>
62-
{:else if data.repo.ahead}
63-
<button on:click={() => api().admin.git.pull()}>Pull</button>
64-
{/if}
65-
66-
<!-- Commit -->
67-
{#if !data.repo.clean}
68-
69-
<h3>Changes</h3>
70-
71-
<ul>
72-
{#each data.repo.changes as change}
73-
{#if change.from}
74-
<li>Rename {change.from} to ({change.path})</li>
75-
{:else if change.index === '?'}
76-
<li>Create {change.path}</li>
77-
{:else if change.index === 'D'}
78-
<li>Delete {change.path}</li>
79-
{:else}
80-
<li>Update {change.path}</li>
81-
{/if}
82-
{/each}
83-
</ul>
84-
85-
<form on:submit|preventDefault={gitCommit}>
86-
<input required type="text" name="commit-message" id="commit-message" placeholder="Commit message" bind:value={commitMessage}>
87-
<input type="submit" value="Commit changes">
88-
</form>
89-
{/if}
90-
91-
{:else}
92-
<h2>Git is currently not in use</h2>
93-
94-
You can use a Git repository to back up your portfolio data. Enter the
95-
clone URL for an empty Git repository and it will be set up for you.
96-
97-
<form on:submit|preventDefault={submitSwitchToGit}>
98-
<input required type="text" name="git-url" id="git-url" placeholder="git@github.com:MaddyGuthridge/Minifolio.git" bind:value={gitUrl}>
99-
<input type="submit" value="Switch to a Git repository">
100-
</form>
101-
{/if}
102-
</div>
103-
<div>
104-
<h2>Reload data from disk</h2>
105-
If you have edited your data manually, you can use this button to
106-
refresh it.
107-
<button on:click={() => api().admin.data.refresh()}>Refresh data</button>
108-
</div>
34+
<GitSettings {data} />
35+
<ChangePassword username={"admin"} />
36+
<LogOutAll />
37+
<ReloadData />
10938
</div>
11039
</Paper>
11140
</div>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script lang="ts">
2+
import api from "$endpoints";
3+
4+
export let username: string;
5+
let originalPassword = "";
6+
let newPassword = "";
7+
let repeatNewPassword = "";
8+
9+
async function submitChangePassword() {
10+
await api().admin.auth.change(username, originalPassword, newPassword);
11+
originalPassword = "";
12+
newPassword = "";
13+
repeatNewPassword = "";
14+
}
15+
</script>
16+
17+
<div>
18+
<h2>Change authentication</h2>
19+
<form on:submit={submitChangePassword}>
20+
<p>
21+
Username
22+
<br />
23+
<input placeholder="Your account username" bind:value={username} />
24+
</p>
25+
<p>
26+
Original password
27+
<br />
28+
<input
29+
type="password"
30+
placeholder="Your original password"
31+
bind:value={originalPassword}
32+
/>
33+
</p>
34+
<p>
35+
New password
36+
<br />
37+
<input
38+
type="password"
39+
placeholder="A unique and secure password"
40+
bind:value={newPassword}
41+
/>
42+
</p>
43+
<p>
44+
Repeat new password
45+
<br />
46+
<input
47+
type="password"
48+
placeholder="Your new password again"
49+
bind:value={repeatNewPassword}
50+
/>
51+
</p>
52+
{#if newPassword != repeatNewPassword}
53+
<p>New passwords much match</p>
54+
{/if}
55+
<p>
56+
<input
57+
type="submit"
58+
value="Change password"
59+
disabled={newPassword != repeatNewPassword}
60+
/>
61+
</p>
62+
</form>
63+
</div>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<script lang="ts">
2+
import api from "$endpoints";
3+
4+
export let data: import("./$types").PageData;
5+
6+
// Git setup
7+
let gitUrl = "";
8+
9+
async function submitSwitchToGit() {
10+
await api().admin.git.init(gitUrl);
11+
}
12+
13+
// Git controls
14+
let commitMessage = "";
15+
16+
async function gitCommit() {
17+
await api().admin.git.commit(commitMessage);
18+
}
19+
</script>
20+
21+
<div>
22+
{#if data.repo}
23+
<h2>Git status</h2>
24+
<p>Current branch: {data.repo.branch}</p>
25+
<p>Current commit: {data.repo.commit}</p>
26+
<p>
27+
{#if data.repo.behind}
28+
{data.repo.behind} commits behind.
29+
{/if}
30+
{#if data.repo.ahead}
31+
{data.repo.ahead} commits ahead.
32+
{/if}
33+
</p>
34+
35+
<!-- Push/pull -->
36+
{#if data.repo.behind}
37+
<button on:click={() => api().admin.git.push()}>Push</button>
38+
{:else if data.repo.ahead}
39+
<button on:click={() => api().admin.git.pull()}>Pull</button>
40+
{/if}
41+
42+
<!-- Commit -->
43+
{#if data.repo.clean}
44+
<h3>Changes</h3>
45+
Working tree clean.
46+
{:else}
47+
<h3>Changes</h3>
48+
49+
<ul>
50+
{#each data.repo.changes as change}
51+
{#if change.from}
52+
<li>Rename {change.from} to ({change.path})</li>
53+
{:else if change.index === "?"}
54+
<li>Create {change.path}</li>
55+
{:else if change.index === "D"}
56+
<li>Delete {change.path}</li>
57+
{:else}
58+
<li>Update {change.path}</li>
59+
{/if}
60+
{/each}
61+
</ul>
62+
63+
<form on:submit|preventDefault={gitCommit}>
64+
<input
65+
required
66+
type="text"
67+
name="commit-message"
68+
id="commit-message"
69+
placeholder="Commit message"
70+
bind:value={commitMessage}
71+
/>
72+
<input type="submit" value="Commit changes" />
73+
</form>
74+
{/if}
75+
{:else}
76+
<h2>Git is currently not in use</h2>
77+
78+
You can use a Git repository to back up your portfolio data. Enter the clone
79+
URL for an empty Git repository and it will be set up for you.
80+
81+
<form on:submit|preventDefault={submitSwitchToGit}>
82+
<input
83+
required
84+
type="text"
85+
name="git-url"
86+
id="git-url"
87+
placeholder="git@github.com:MaddyGuthridge/Minifolio.git"
88+
bind:value={gitUrl}
89+
/>
90+
<input type="submit" value="Switch to a Git repository" />
91+
</form>
92+
{/if}
93+
</div>

src/routes/admin/LogOutAll.svelte

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script lang="ts">
2+
import { goto } from "$app/navigation";
3+
import api from "$endpoints";
4+
5+
async function logOut() {
6+
await api().admin.auth.revoke();
7+
goto("/admin/login");
8+
}
9+
</script>
10+
11+
<div>
12+
<h2>Log out of all sessions</h2>
13+
<p>This will sign you out on all of your devices (including this one).</p>
14+
<button on:click={logOut}>Log out of all sessions</button>
15+
</div>

src/routes/admin/ReloadData.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
import api from "$endpoints";
3+
</script>
4+
5+
<div>
6+
<h2>Reload data from disk</h2>
7+
If you have edited your data manually, you can use this button to refresh it.
8+
<button on:click={() => api().admin.data.refresh()}>Refresh data</button>
9+
</div>

src/routes/admin/login/+page.server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export async function load(req) {
1212
loggedIn = true;
1313
} catch { /* empty */ }
1414
if (loggedIn) {
15-
redirect(303, '/admin');
15+
// If they are logged in, redirect them to the `from` URL if it exists.
16+
redirect(303, req.url.searchParams.get("from") || '/');
1617
}
1718
const globals = await getPortfolioGlobals();
1819
return { globals };

0 commit comments

Comments
 (0)