forked from snxraven/p2ns
Modernize Holepunch stack for HyperDB 6 and current replication APIs
Align P2NS with current Autopass, Hypercore, Hyperdrive, and Protomux behavior: add plugin DB recovery for incompatible persisted HyperDB data, stop unconditional spec rebuilds via fingerprinting, update SDK replication paths, and align proxy invite channels with p2ns.core-* protocols without replicating Autopass over the p2ns topic.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "File Drop",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"domain": "file.drop",
|
||||
"enabled": false,
|
||||
"description": "Temporary 24-hour file storage with shareable links",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Global Profile",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.2",
|
||||
"domain": "global.profile",
|
||||
"enabled": true,
|
||||
"description": "Universal user identity system for all P2NS plugins with P2P profile replication",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
const sdk = require('../../includes/plugins/sdk');
|
||||
const crypto = require('crypto');
|
||||
const { isIncompatibleDbError, dbErrorResponse } = require('./db-errors');
|
||||
|
||||
// Avatar sizes to generate
|
||||
const AVATAR_SIZES = [16, 32, 64, 128, 256, 512];
|
||||
@@ -36,6 +37,10 @@ async function getProfileFromDB(peerId) {
|
||||
const profile = await sdk.db.get('@profile/profiles', { peerId });
|
||||
return profile;
|
||||
} catch (err) {
|
||||
if (isIncompatibleDbError(err)) {
|
||||
sdk.log.warn('global.profile', `Incompatible DB data while reading profile: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
// Silently return null if database not initialized or timeout - this is expected during startup
|
||||
if (err.message && (err.message.includes('Database not initialized') || err.message.includes('timeout'))) {
|
||||
sdk.log.debug('global.profile', `Database not ready for ${peerId ? peerId.slice(0, 16) : 'unknown'}: ${err.message}`);
|
||||
@@ -170,6 +175,10 @@ async function getAllProfilesFromDB() {
|
||||
sdk.log.debug('global.profile', `Found ${profiles?.length || 0} profiles in database`);
|
||||
return profiles || [];
|
||||
} catch (err) {
|
||||
if (isIncompatibleDbError(err)) {
|
||||
sdk.log.warn('global.profile', `Incompatible DB data while listing profiles: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
// Silently return empty array if database not initialized or timeout - this is expected during startup
|
||||
if (err.message && (err.message.includes('Database not initialized') || err.message.includes('timeout') || err.message.includes('not available'))) {
|
||||
sdk.log.debug('global.profile', `Database not ready when getting all profiles: ${err.message}`);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Shared DB error handling for global.profile routes.
|
||||
*/
|
||||
|
||||
const { isIncompatibleDbError } = require('../../includes/plugins/db-recovery');
|
||||
|
||||
function isDbNotReadyError(err) {
|
||||
const msg = err && err.message ? err.message : '';
|
||||
return (
|
||||
msg.includes('Database not initialized') ||
|
||||
msg.includes('timeout') ||
|
||||
msg.includes('not ready')
|
||||
);
|
||||
}
|
||||
|
||||
function dbErrorResponse(err) {
|
||||
if (isIncompatibleDbError(err)) {
|
||||
return {
|
||||
status: 503,
|
||||
body: {
|
||||
error: 'Profile database is being reset after a schema upgrade',
|
||||
code: 'db_migration',
|
||||
hint: 'Retry in a few seconds. If this persists, delete plugin-sites/global.profile/db/ and restart P2NS.'
|
||||
}
|
||||
};
|
||||
}
|
||||
if (isDbNotReadyError(err)) {
|
||||
return {
|
||||
status: 503,
|
||||
body: {
|
||||
error: 'Profile database not ready',
|
||||
code: 'db_not_ready'
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 500,
|
||||
body: { error: err.message || 'Database error' }
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isIncompatibleDbError,
|
||||
isDbNotReadyError,
|
||||
dbErrorResponse
|
||||
};
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
const sdk = require('../../../includes/plugins/sdk');
|
||||
const { dbErrorResponse } = require('../db-errors');
|
||||
|
||||
/**
|
||||
* Handle fields-related routes
|
||||
@@ -18,10 +19,8 @@ async function handleFieldsRoutes(req, res, path, query, method) {
|
||||
return sdk.router.json(res, { fields: fields || [] });
|
||||
} catch (err) {
|
||||
sdk.log.error('global.profile', `Error getting fields: ${err.message}`);
|
||||
if (err.message && err.message.includes('Database not initialized')) {
|
||||
return sdk.router.json(res, { fields: [], error: 'Database not ready' }, 503);
|
||||
}
|
||||
return sdk.router.error(res, 'Failed to retrieve fields', 500);
|
||||
const { status, body } = dbErrorResponse(err);
|
||||
return sdk.router.json(res, { fields: [], ...body }, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
const sdk = require('../../../includes/plugins/sdk');
|
||||
const { getLocalPeerId } = require('../utils');
|
||||
const { getProfileFromDB, saveProfileToDB } = require('../database');
|
||||
const { dbErrorResponse } = require('../db-errors');
|
||||
const { broadcastProfileUpdate } = require('../websocket');
|
||||
|
||||
/**
|
||||
@@ -83,7 +84,8 @@ async function handleProfileRoutes(req, res, path, query, method) {
|
||||
}
|
||||
sdk.log.error('global.profile', `Error getting profile: ${err.message}`);
|
||||
if (!res.headersSent) {
|
||||
return router.error(res, 'Failed to retrieve profile', 500, req);
|
||||
const { status, body } = dbErrorResponse(err);
|
||||
return router.json(res, body, status, req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
const sdk = require('../../../includes/plugins/sdk');
|
||||
const { getAllProfilesFromDB } = require('../database');
|
||||
const { dbErrorResponse } = require('../db-errors');
|
||||
|
||||
/**
|
||||
* Handle profiles-related routes
|
||||
@@ -102,7 +103,8 @@ async function handleProfilesRoutes(req, res, path, query, method) {
|
||||
});
|
||||
} catch (err) {
|
||||
sdk.log.error('global.profile', `Error getting profiles: ${err.message}`);
|
||||
return sdk.router.error(res, err.message, 500);
|
||||
const { status, body } = dbErrorResponse(err);
|
||||
return sdk.router.json(res, body, status, req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user