forked from snxraven/p2ns
Fix DB peer.paste
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Peer Paste",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"domain": "peer.paste",
|
||||
"enabled": true,
|
||||
"description": "Temporary P2P text snippets with expiration and burn-after-read options",
|
||||
|
||||
@@ -219,7 +219,28 @@ function shouldDeleteAfterRead(paste, nextReadCount) {
|
||||
}
|
||||
|
||||
async function deletePaste(paste) {
|
||||
await sdk.db.delete(COLLECTION, { id: paste.id });
|
||||
await withDbRecovery('deletePaste', () => sdk.db.delete(COLLECTION, { id: paste.id }));
|
||||
}
|
||||
|
||||
function isUnknownCollectionError(err) {
|
||||
const msg = (err && err.message) || '';
|
||||
return /unknown collection/i.test(msg);
|
||||
}
|
||||
|
||||
async function withDbRecovery(context, fn) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (err) {
|
||||
if (!isUnknownCollectionError(err)) throw err;
|
||||
sdk.log.warn('peer.paste', `${context}: unknown collection, running db.update() then retry`);
|
||||
try {
|
||||
await sdk.db.update();
|
||||
await sdk.db.ready(10000);
|
||||
} catch (refreshErr) {
|
||||
sdk.log.warn('peer.paste', `${context}: db refresh failed: ${refreshErr.message}`);
|
||||
}
|
||||
return fn();
|
||||
}
|
||||
}
|
||||
|
||||
function isQueryTimeoutError(err) {
|
||||
@@ -236,7 +257,7 @@ async function safeFindPastes(options = {}) {
|
||||
} = options;
|
||||
|
||||
const queryOptions = { limit: maxEntries };
|
||||
const run = async () => sdk.db.find(COLLECTION, {}, queryOptions);
|
||||
const run = async () => withDbRecovery(context, () => sdk.db.find(COLLECTION, {}, queryOptions));
|
||||
|
||||
try {
|
||||
let rows = await run();
|
||||
@@ -354,8 +375,10 @@ async function createPaste(req, res) {
|
||||
expiresAt
|
||||
};
|
||||
|
||||
await sdk.db.insert(COLLECTION, paste);
|
||||
await sdk.db.flush();
|
||||
await withDbRecovery('createPaste.insert', async () => {
|
||||
await sdk.db.insert(COLLECTION, paste);
|
||||
await sdk.db.flush();
|
||||
});
|
||||
|
||||
return sdk.router.json(res, {
|
||||
success: true,
|
||||
@@ -404,7 +427,7 @@ async function listPastes(req, res, ownerOnly = false) {
|
||||
}
|
||||
|
||||
async function getPasteMetadata(res, pasteId) {
|
||||
const raw = await sdk.db.get(COLLECTION, { id: pasteId });
|
||||
const raw = await withDbRecovery('getPasteMetadata.get', () => sdk.db.get(COLLECTION, { id: pasteId }));
|
||||
const paste = normalizeStoredPaste(raw);
|
||||
if (!paste) return sdk.router.notFound(res, 'Paste not found');
|
||||
let attachments = [];
|
||||
@@ -426,7 +449,7 @@ async function getPasteMetadata(res, pasteId) {
|
||||
}
|
||||
|
||||
async function getPasteAttachments(res, pasteId, attachmentId = null) {
|
||||
const raw = await sdk.db.get(COLLECTION, { id: pasteId });
|
||||
const raw = await withDbRecovery('getPasteAttachments.get', () => sdk.db.get(COLLECTION, { id: pasteId }));
|
||||
const paste = normalizeStoredPaste(raw);
|
||||
if (!paste) return sdk.router.notFound(res, 'Paste not found');
|
||||
let attachments = [];
|
||||
@@ -451,7 +474,7 @@ async function getPasteAttachments(res, pasteId, attachmentId = null) {
|
||||
}
|
||||
|
||||
async function consumePaste(res, pasteId) {
|
||||
const raw = await sdk.db.get(COLLECTION, { id: pasteId });
|
||||
const raw = await withDbRecovery('consumePaste.get', () => sdk.db.get(COLLECTION, { id: pasteId }));
|
||||
const paste = normalizeStoredPaste(raw);
|
||||
if (!paste || !isConsumable(paste)) {
|
||||
return sdk.router.notFound(res, 'Paste not found or expired');
|
||||
@@ -463,12 +486,12 @@ async function consumePaste(res, pasteId) {
|
||||
if (consumed) {
|
||||
await deletePaste(paste);
|
||||
} else {
|
||||
await sdk.db.insert(COLLECTION, {
|
||||
await withDbRecovery('consumePaste.insert', () => sdk.db.insert(COLLECTION, {
|
||||
...paste,
|
||||
readCount: nextReadCount
|
||||
});
|
||||
}));
|
||||
}
|
||||
await sdk.db.flush();
|
||||
await withDbRecovery('consumePaste.flush', () => sdk.db.flush());
|
||||
|
||||
const encryptedPayload = decodeEncryptedContent(paste.content);
|
||||
let attachments = [];
|
||||
@@ -494,7 +517,7 @@ async function updatePaste(req, res, pasteId) {
|
||||
const localPeer = await sdk.auth.requireLocalPeer(req, res);
|
||||
if (!localPeer) return true;
|
||||
|
||||
const existing = normalizeStoredPaste(await sdk.db.get(COLLECTION, { id: pasteId }));
|
||||
const existing = normalizeStoredPaste(await withDbRecovery('updatePaste.get', () => sdk.db.get(COLLECTION, { id: pasteId })));
|
||||
if (!existing) return sdk.router.notFound(res, 'Paste not found');
|
||||
if (existing.ownerPeerId && existing.ownerPeerId !== localPeer) {
|
||||
return sdk.router.forbidden(res, 'Only the owner can update this paste');
|
||||
@@ -539,8 +562,10 @@ async function updatePaste(req, res, pasteId) {
|
||||
: existing.expiresAt
|
||||
};
|
||||
|
||||
await sdk.db.insert(COLLECTION, updated);
|
||||
await sdk.db.flush();
|
||||
await withDbRecovery('updatePaste.insert', async () => {
|
||||
await sdk.db.insert(COLLECTION, updated);
|
||||
await sdk.db.flush();
|
||||
});
|
||||
|
||||
return sdk.router.json(res, {
|
||||
success: true,
|
||||
@@ -552,7 +577,7 @@ async function deletePasteRoute(req, res, pasteId) {
|
||||
const localPeer = await sdk.auth.requireLocalPeer(req, res);
|
||||
if (!localPeer) return true;
|
||||
|
||||
const paste = normalizeStoredPaste(await sdk.db.get(COLLECTION, { id: pasteId }));
|
||||
const paste = normalizeStoredPaste(await withDbRecovery('deletePasteRoute.get', () => sdk.db.get(COLLECTION, { id: pasteId })));
|
||||
if (!paste) return sdk.router.notFound(res, 'Paste not found');
|
||||
if (paste.ownerPeerId && paste.ownerPeerId !== localPeer) {
|
||||
return sdk.router.forbidden(res, 'Only the owner can delete this paste');
|
||||
@@ -1139,8 +1164,10 @@ function setupWebSocketHandlers() {
|
||||
expiresAt
|
||||
};
|
||||
|
||||
await sdk.db.insert(COLLECTION, paste);
|
||||
await sdk.db.flush();
|
||||
await withDbRecovery('ws.create.insert', async () => {
|
||||
await sdk.db.insert(COLLECTION, paste);
|
||||
await sdk.db.flush();
|
||||
});
|
||||
metrics.createCount += 1;
|
||||
|
||||
sdk.websocket.send(ws, {
|
||||
@@ -1164,7 +1191,7 @@ function setupWebSocketHandlers() {
|
||||
return;
|
||||
}
|
||||
const localPeer = sdk.state.localPeerId || '';
|
||||
const paste = await sdk.db.get(COLLECTION, { id: pasteId });
|
||||
const paste = await withDbRecovery('ws.destroy.get', () => sdk.db.get(COLLECTION, { id: pasteId }));
|
||||
if (!paste) {
|
||||
sdk.websocket.send(ws, { type: 'error', error: 'Paste not found', requestId: message.requestId || null });
|
||||
return;
|
||||
@@ -1264,7 +1291,7 @@ async function handler(req, res) {
|
||||
|
||||
if (path.startsWith('p/') && method === 'GET') {
|
||||
const pasteId = path.slice('p/'.length);
|
||||
const paste = await sdk.db.get(COLLECTION, { id: pasteId });
|
||||
const paste = await withDbRecovery('viewPaste.get', () => sdk.db.get(COLLECTION, { id: pasteId }));
|
||||
if (!paste || !isConsumable(paste)) {
|
||||
return sdk.router.notFound(res, 'Paste not found or expired');
|
||||
}
|
||||
@@ -1274,9 +1301,9 @@ async function handler(req, res) {
|
||||
if (consumed) {
|
||||
await deletePaste(paste);
|
||||
} else {
|
||||
await sdk.db.insert(COLLECTION, { ...paste, readCount: nextReadCount });
|
||||
await withDbRecovery('viewPaste.insert', () => sdk.db.insert(COLLECTION, { ...paste, readCount: nextReadCount }));
|
||||
}
|
||||
await sdk.db.flush();
|
||||
await withDbRecovery('viewPaste.flush', () => sdk.db.flush());
|
||||
return sdk.router.html(res, renderPastePage(paste), 200);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user