You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
3.9 KiB

import { config } from './env.mjs';
export class PocketBaseError extends Error {
constructor(status, code, message, data) {
super(message);
this.name = 'PocketBaseError';
this.status = status;
this.code = code;
this.data = data;
}
}
let superuserAuth = null;
export function fileUrl(collection, recordId, filename, thumb = '') {
const url = `${config.pocketBaseUrl}/api/files/${encodeURIComponent(collection)}/${encodeURIComponent(
recordId
)}/${encodeURIComponent(filename)}`;
return thumb ? `${url}?thumb=${encodeURIComponent(thumb)}` : url;
}
export async function pbPublic(path, options = {}) {
return pbRequest(path, options);
}
export async function pbUser(path, token, options = {}) {
return pbRequest(path, { ...options, token });
}
export async function pbSuper(path, options = {}) {
const token = await getSuperuserToken();
return pbRequest(path, { ...options, token });
}
export async function listRecords(collection, options = {}) {
const query = {
page: options.page || 1,
perPage: options.perPage || 50,
sort: options.sort,
filter: options.filter,
expand: options.expand
};
return pbSuper(`/api/collections/${collection}/records`, { query });
}
export async function firstRecord(collection, filter) {
const result = await listRecords(collection, { filter, perPage: 1 });
return result.items?.[0] || null;
}
export async function getRecord(collection, id) {
return pbSuper(`/api/collections/${collection}/records/${encodeURIComponent(id)}`);
}
export async function createRecord(collection, body) {
return pbSuper(`/api/collections/${collection}/records`, {
method: 'POST',
body
});
}
export async function updateRecord(collection, id, body) {
return pbSuper(`/api/collections/${collection}/records/${encodeURIComponent(id)}`, {
method: 'PATCH',
body
});
}
export async function updateRecordForm(collection, id, body) {
return pbSuper(`/api/collections/${collection}/records/${encodeURIComponent(id)}`, {
method: 'PATCH',
body
});
}
async function getSuperuserToken() {
if (superuserAuth && superuserAuth.expiresAt > Date.now() + 30_000) {
return superuserAuth.token;
}
const response = await pbRequest('/api/collections/_superusers/auth-with-password', {
method: 'POST',
body: {
identity: config.pocketBaseSuperuserEmail,
password: config.pocketBaseSuperuserPassword
}
});
superuserAuth = {
token: response.token,
expiresAt: tokenExpiresAt(response.token)
};
return response.token;
}
async function pbRequest(path, options = {}) {
const url = new URL(path, config.pocketBaseUrl);
for (const [key, value] of Object.entries(options.query || {})) {
if (value != null && value !== '') url.searchParams.set(key, String(value));
}
const headers = new Headers(options.headers || {});
if (options.token) headers.set('authorization', `Bearer ${options.token}`);
let body = options.body;
if (body && !(body instanceof FormData) && typeof body !== 'string') {
headers.set('content-type', 'application/json');
body = JSON.stringify(body);
}
const response = await fetch(url, {
method: options.method || (body ? 'POST' : 'GET'),
headers,
body
});
const text = await response.text();
const data = text ? parseJson(text) : null;
if (!response.ok) {
throw new PocketBaseError(
response.status,
data?.code || data?.status || 'pocketbase_error',
data?.message || `PocketBase request failed with ${response.status}.`,
data?.data || data
);
}
return data;
}
function parseJson(text) {
try {
return JSON.parse(text);
} catch {
return { message: text };
}
}
function tokenExpiresAt(token) {
const [, payload] = token.split('.');
if (!payload) return Date.now() + 5 * 60_000;
try {
const decoded = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
return typeof decoded.exp === 'number' ? decoded.exp * 1000 : Date.now() + 5 * 60_000;
} catch {
return Date.now() + 5 * 60_000;
}
}

Powered by TurnKey Linux.