Re-authenticate in place when the Perforce ticket expires
Perforce ends the ticket on its own schedule, so a long day outlives a login. Every command then failed and the only way back was Exit + restart, which threw away the scope, selection and open panels. - p4.ts routes all ~110 commands through one invoke wrapper that recognises an auth failure (isAuthError) and raises onAuthLost, so no call site can miss it. p4_login / p4_restore / p4_clients are exempt: they report bad credentials inline and would otherwise loop the prompt. - The server poll no longer treats an expired ticket as an outage. The server answered, it just refused us — the offline banner it used to raise never cleared, since polling stayed refused forever. - ReauthModal asks for the password over the running app, keeping all state, and offers Sign out as the way back to the Connect screen. The failed command is NOT retried automatically — re-auth then refresh only. Replaying a half-finished submit or revert unattended is worse than asking. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
28
src/p4.ts
28
src/p4.ts
@ -1,5 +1,31 @@
|
||||
// Typed wrappers around the Rust p4 commands.
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { invoke as rawInvoke } from "@tauri-apps/api/core";
|
||||
|
||||
/** True when p4 rejected us for auth reasons — an expired or dropped ticket —
|
||||
* as opposed to the server being unreachable. Perforce expires tickets on its
|
||||
* own schedule (often 12h), so a workday can outlive a login. */
|
||||
export function isAuthError(e: unknown): boolean {
|
||||
return /P4PASSWD|invalid or unset|session has expired|session was logged out|please login again/i
|
||||
.test(String(e));
|
||||
}
|
||||
|
||||
// Set by the app: called when any command comes back with an auth failure, so
|
||||
// the UI can ask for the password again instead of dead-ending on every action.
|
||||
let authLost: (() => void) | null = null;
|
||||
export function onAuthLost(fn: (() => void) | null) { authLost = fn; }
|
||||
|
||||
// Commands that legitimately report bad credentials — the login screen shows
|
||||
// those inline, and firing the re-auth prompt from them would loop.
|
||||
const AUTH_CMDS = new Set(["p4_login", "p4_restore", "p4_clients"]);
|
||||
|
||||
/** Every p4 command funnels through here so a lost ticket is caught in one
|
||||
* place rather than at each of the ~110 call sites. */
|
||||
function invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T> {
|
||||
return rawInvoke<T>(cmd, args).catch((e) => {
|
||||
if (!AUTH_CMDS.has(cmd) && isAuthError(e)) authLost?.();
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
export interface P4Info {
|
||||
serverAddress?: string;
|
||||
|
||||
Reference in New Issue
Block a user