chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { useState } from 'react';
import { Button } from '#/components/atoms/button.jsx';
import { Notice } from '#/components/atoms/notice.jsx';
import { Panel } from '#/components/atoms/panel.jsx';
import { ErrorNotice } from '#/components/molecules/error-notice.jsx';
import { Section } from '#/components/molecules/section.jsx';
import { TextField } from '#/components/molecules/text-field.jsx';
import { post } from '#/lib/api.js';
/**
* Change the account password. The current password is required even inside
* a signed-in session, so a stolen cookie alone cannot rotate the
* credential. Open sessions and app passwords stay valid.
*/
export function PasswordCard() {
const [current, setCurrent] = useState('');
const [next, setNext] = useState('');
const [confirm, setConfirm] = useState('');
const [busy, setBusy] = useState(false);
const [error, setError] = useState('');
const [done, setDone] = useState(false);
const submit = async (/** @type {import('react').FormEvent} */ event) => {
event.preventDefault();
setError('');
setDone(false);
if (next !== confirm) {
setError('The new passwords do not match.');
return;
}
setBusy(true);
try {
await post('/password', { currentPassword: current, newPassword: next });
setDone(true);
setCurrent('');
setNext('');
setConfirm('');
} catch (caught) {
setError(String(caught?.message || caught));
} finally {
setBusy(false);
}
};
return (
<Panel>
<Section
title="Password"
description="Signs in to this server and its account pages."
>
<form onSubmit={submit} className="flex flex-col gap-4">
<ErrorNotice error={error} />
{done && (
<Notice variant="info">
Password changed. Open sessions stay signed in.
</Notice>
)}
<TextField
label="Current password"
type="password"
autoComplete="current-password"
value={current}
onChange={(e) => setCurrent(e.target.value)}
required
/>
<TextField
label="New password"
type="password"
autoComplete="new-password"
value={next}
onChange={(e) => setNext(e.target.value)}
minLength={8}
hint="At least 8 characters."
required
/>
<TextField
label="Confirm new password"
type="password"
autoComplete="new-password"
value={confirm}
onChange={(e) => setConfirm(e.target.value)}
required
/>
<div>
<Button type="submit" disabled={busy}>
Change password
</Button>
</div>
</form>
</Section>
</Panel>
);
}