propose new chore update fix
This commit is contained in:
@@ -30,9 +30,16 @@ export function pbUser(event: RequestEvent) {
|
|||||||
// Superuser PB client (memoized). Reserved for server-only privileged
|
// Superuser PB client (memoized). Reserved for server-only privileged
|
||||||
// operations that must bypass collection rules: creating child users, minting
|
// operations that must bypass collection rules: creating child users, minting
|
||||||
// OTP-login tokens, and verifying OTPs against the superuser-only otp.
|
// OTP-login tokens, and verifying OTPs against the superuser-only otp.
|
||||||
|
//
|
||||||
|
// NOTE: the auth token expires (prod PB showed ~36h). A stale memoized client
|
||||||
|
// goes out UNAUTHENTICATED, and PocketBase answers unauthenticated getOne()
|
||||||
|
// calls with 404 "resource wasn't found" — which the toggle path misreads as
|
||||||
|
// CHORE_GONE for every chore. So always re-auth when the stored token is no
|
||||||
|
// longer valid instead of reusing a dead client.
|
||||||
let superClient: PocketBase | null = null;
|
let superClient: PocketBase | null = null;
|
||||||
export async function createSuperClient() {
|
export async function createSuperClient() {
|
||||||
if (superClient) return superClient;
|
if (superClient?.authStore.isValid) return superClient;
|
||||||
|
superClient = null;
|
||||||
const pb = new PocketBase(PB_ENDPOINT);
|
const pb = new PocketBase(PB_ENDPOINT);
|
||||||
pb.autoCancellation(false);
|
pb.autoCancellation(false);
|
||||||
await pb.collection('_superusers').authWithPassword(String(PB_EMAIL), String(PB_PASSWORD));
|
await pb.collection('_superusers').authWithPassword(String(PB_EMAIL), String(PB_PASSWORD));
|
||||||
@@ -40,29 +47,43 @@ export async function createSuperClient() {
|
|||||||
return pb;
|
return pb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run a superuser op, retrying once with a freshly-authenticated client when
|
||||||
|
// the first attempt hits an auth-shaped failure. A locally-valid token can
|
||||||
|
// still be dead server-side (restart/revoked secret rotation), and PB
|
||||||
|
// surfaces that as 401/403 — or as 404 when a viewRule then denies access.
|
||||||
|
async function withSuperRetry<T>(op: (pb: PocketBase) => Promise<T>): Promise<T> {
|
||||||
|
try {
|
||||||
|
return await op(await createSuperClient());
|
||||||
|
} catch (e: any) {
|
||||||
|
const status = e?.status;
|
||||||
|
if (status === 401 || status === 403 || status === 404) {
|
||||||
|
superClient = null;
|
||||||
|
return await op(await createSuperClient());
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Superuser CRUD facade, built on the memoized SDK superuser client. All
|
// Superuser CRUD facade, built on the memoized SDK superuser client. All
|
||||||
// server PB access (authenticated user + superuser) lives in this one module.
|
// server PB access (authenticated user + superuser) lives in this one module.
|
||||||
export const pbAdmin = {
|
export const pbAdmin = {
|
||||||
async getList(collection: string, filter = '') {
|
async getList(collection: string, filter = '') {
|
||||||
const pb = await createSuperClient();
|
return withSuperRetry((pb) => {
|
||||||
const options: { filter?: string } = {};
|
const options: { filter?: string } = {};
|
||||||
if (filter) options.filter = filter;
|
if (filter) options.filter = filter;
|
||||||
return pb.collection(collection).getFullList(options);
|
return pb.collection(collection).getFullList(options);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
async getOne(collection: string, id: string) {
|
async getOne(collection: string, id: string) {
|
||||||
const pb = await createSuperClient();
|
return withSuperRetry((pb) => pb.collection(collection).getOne(id));
|
||||||
return pb.collection(collection).getOne(id);
|
|
||||||
},
|
},
|
||||||
async create(collection: string, data: Record<string, unknown>) {
|
async create(collection: string, data: Record<string, unknown>) {
|
||||||
const pb = await createSuperClient();
|
return withSuperRetry((pb) => pb.collection(collection).create(data));
|
||||||
return pb.collection(collection).create(data);
|
|
||||||
},
|
},
|
||||||
async update(collection: string, id: string, data: Record<string, unknown>) {
|
async update(collection: string, id: string, data: Record<string, unknown>) {
|
||||||
const pb = await createSuperClient();
|
return withSuperRetry((pb) => pb.collection(collection).update(id, data));
|
||||||
return pb.collection(collection).update(id, data);
|
|
||||||
},
|
},
|
||||||
async remove(collection: string, id: string) {
|
async remove(collection: string, id: string) {
|
||||||
const pb = await createSuperClient();
|
return withSuperRetry((pb) => pb.collection(collection).delete(id));
|
||||||
return pb.collection(collection).delete(id);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user