ask for the URL first, and offer what tea already knows
The form asked for a URL and a token together, on a fresh instance, while the machine in front of it often had both on disk already — somebody had run `tea login` by hand. Two changes. One thing at a time: until the platform knows where Gitea is, a token cannot be checked, because there is nothing to check it against. So the owner names the instance first, and only then does the page ask for — or discover — a token. That step now probes /api/v1/version unauthenticated, since demanding a token to validate a URL refuses a good URL for the wrong reason and sends whoever typed it looking for a credential problem. And it reads tea's own config when the platform has no instance yet, offering the URL it finds. A machine that already has a working login should not be asked to retype what it can see. Members skip the first step entirely — the instance is the owner's.
This commit is contained in:
+30
-9
@@ -20,6 +20,8 @@ export type ConnectionState = {
|
||||
url?: string | null;
|
||||
user?: string | null;
|
||||
isOwner?: boolean;
|
||||
/** Whether the OWNER has set the instance URL. Members inherit it and never set one. */
|
||||
instanceConfigured?: boolean;
|
||||
/**
|
||||
* A token is stored but cannot be decrypted.
|
||||
*
|
||||
@@ -30,7 +32,7 @@ export type ConnectionState = {
|
||||
*/
|
||||
unreadable?: boolean;
|
||||
/** What `tea` already has for this instance. Null when it has no login for it. */
|
||||
tea?: { name: string; user: string | null; hasToken: boolean } | null;
|
||||
tea?: { name: string; url?: string; user: string | null; hasToken: boolean } | null;
|
||||
/** False when this account has no OS user, so there is nowhere to write a config. */
|
||||
canStoreToken?: boolean;
|
||||
};
|
||||
@@ -43,7 +45,10 @@ export const GiteaConnection = ({ state, onSaved }: Props) => {
|
||||
// Loopback is the right default and worth putting in front of someone: the sidecar fetches
|
||||
// SERVER-SIDE, so `localhost` means the machine Officer runs on. It needs no TLS and never leaves the
|
||||
// box — where the public URL would take the same traffic out and back.
|
||||
const [url, setUrl] = useState(state.url ?? 'http://localhost:3000');
|
||||
// Seeded from what tea already knows before falling back to a guess. On a machine where somebody ran
|
||||
// `tea login` by hand, the URL is already on disk and asking for it again is asking for something the
|
||||
// box can see.
|
||||
const [url, setUrl] = useState(state.url ?? state.tea?.url ?? 'http://localhost:3000');
|
||||
const [token, setToken] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -63,6 +68,13 @@ export const GiteaConnection = ({ state, onSaved }: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ONE THING AT A TIME. Until the platform knows where Gitea is, a token cannot be checked — there is
|
||||
// nothing to check it against — and once the URL is known, tea may already hold a working token, in
|
||||
// which case the second step never appears at all.
|
||||
//
|
||||
// Members skip step one entirely: the instance is the owner's.
|
||||
const needsUrl = state.isOwner !== false && !state.instanceConfigured;
|
||||
|
||||
const save = async () => {
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
@@ -86,13 +98,15 @@ export const GiteaConnection = ({ state, onSaved }: Props) => {
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{state.unreadable
|
||||
? "The stored token can no longer be read — this server's encryption key changed. Nothing else is lost; paste it again."
|
||||
: state.isOwner === false
|
||||
? 'Your own access token. The instance is already set.'
|
||||
: 'Where Gitea runs, and a token for it.'}
|
||||
: needsUrl
|
||||
? 'Where Gitea runs. Officer reaches it from the server, so localhost means the machine Officer runs on.'
|
||||
: state.isOwner === false
|
||||
? 'Your own access token. The instance is already set.'
|
||||
: 'Your own access token for this instance.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{state.isOwner !== false && (
|
||||
{needsUrl && (
|
||||
<label className="space-y-1.5">
|
||||
<span className="text-xs font-medium text-muted-foreground">Instance URL</span>
|
||||
<Input value={url} onChange={(ev) => setUrl(ev.target.value)} placeholder="http://localhost:3000" />
|
||||
@@ -103,15 +117,19 @@ export const GiteaConnection = ({ state, onSaved }: Props) => {
|
||||
</label>
|
||||
)}
|
||||
|
||||
{/* Not shown while the instance is still unknown: a token cannot be validated without one, and
|
||||
tea may already hold one for whatever URL is entered above. */}
|
||||
{needsUrl ? null : (
|
||||
<label className="space-y-1.5">
|
||||
<span className="text-xs font-medium text-muted-foreground">Access token</span>
|
||||
<Input
|
||||
type="password"
|
||||
value={token}
|
||||
onChange={(ev) => setToken(ev.target.value)}
|
||||
placeholder={state.configured && !state.unreadable ? '•••••••• (unchanged)' : 'Settings → Applications in Gitea'}
|
||||
placeholder={state.tea?.hasToken && !state.unreadable ? '•••••••• (unchanged)' : 'Settings → Applications in Gitea'}
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
|
||||
{/* What tea already has, read from the account's own config. On a machine where someone ran
|
||||
`tea login` by hand, this says so instead of demanding a token they already gave. */}
|
||||
@@ -139,9 +157,12 @@ export const GiteaConnection = ({ state, onSaved }: Props) => {
|
||||
{error && <p className="text-xs text-destructive">{error}</p>}
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button onClick={save} disabled={busy || ((!state.configured || state.unreadable) && !token)}>
|
||||
<Button
|
||||
onClick={save}
|
||||
disabled={busy || (needsUrl ? !url.trim() : !state.tea?.hasToken && !token)}
|
||||
>
|
||||
{busy ? <Loader2 className="mr-2 h-3.5 w-3.5 animate-spin" /> : null}
|
||||
{state.configured ? 'Save' : 'Connect'}
|
||||
{needsUrl ? 'Continue' : state.configured ? 'Save' : 'Connect'}
|
||||
</Button>
|
||||
{/* Only offered when there is something to test. */}
|
||||
{state.tea?.hasToken ? (
|
||||
|
||||
Reference in New Issue
Block a user