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
|
||||
// operations that must bypass collection rules: creating child users, minting
|
||||
// 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;
|
||||
export async function createSuperClient() {
|
||||
if (superClient) return superClient;
|
||||
if (superClient?.authStore.isValid) return superClient;
|
||||
superClient = null;
|
||||
const pb = new PocketBase(PB_ENDPOINT);
|
||||
pb.autoCancellation(false);
|
||||
await pb.collection('_superusers').authWithPassword(String(PB_EMAIL), String(PB_PASSWORD));
|
||||
@@ -40,29 +47,43 @@ export async function createSuperClient() {
|
||||
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
|
||||
// server PB access (authenticated user + superuser) lives in this one module.
|
||||
export const pbAdmin = {
|
||||
async getList(collection: string, filter = '') {
|
||||
const pb = await createSuperClient();
|
||||
return withSuperRetry((pb) => {
|
||||
const options: { filter?: string } = {};
|
||||
if (filter) options.filter = filter;
|
||||
return pb.collection(collection).getFullList(options);
|
||||
});
|
||||
},
|
||||
async getOne(collection: string, id: string) {
|
||||
const pb = await createSuperClient();
|
||||
return pb.collection(collection).getOne(id);
|
||||
return withSuperRetry((pb) => pb.collection(collection).getOne(id));
|
||||
},
|
||||
async create(collection: string, data: Record<string, unknown>) {
|
||||
const pb = await createSuperClient();
|
||||
return pb.collection(collection).create(data);
|
||||
return withSuperRetry((pb) => pb.collection(collection).create(data));
|
||||
},
|
||||
async update(collection: string, id: string, data: Record<string, unknown>) {
|
||||
const pb = await createSuperClient();
|
||||
return pb.collection(collection).update(id, data);
|
||||
return withSuperRetry((pb) => pb.collection(collection).update(id, data));
|
||||
},
|
||||
async remove(collection: string, id: string) {
|
||||
const pb = await createSuperClient();
|
||||
return pb.collection(collection).delete(id);
|
||||
return withSuperRetry((pb) => pb.collection(collection).delete(id));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user