This commit is contained in:
@@ -35,8 +35,9 @@ if (FEDERATION_DOMAIN) {
|
||||
publicKeyPem = crypto.createPublicKey(currentPrivateKey).export({ type: 'spki', format: 'pem' });
|
||||
}
|
||||
|
||||
// Instance discovery cache (domain → { baseUrl, publicKey })
|
||||
// Instance discovery cache (domain → { baseUrl, publicKey, cachedAt })
|
||||
const discoveryCache = new Map();
|
||||
const DISCOVERY_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
/**
|
||||
* Get this instance's federation domain.
|
||||
@@ -101,19 +102,20 @@ export function verifyPayload(payload, signature, remotePublicKeyPem) {
|
||||
* @returns {Promise<{ baseUrl: string, publicKey: string }>}
|
||||
*/
|
||||
export async function discoverInstance(domain) {
|
||||
if (discoveryCache.has(domain)) {
|
||||
return discoveryCache.get(domain);
|
||||
const cached = discoveryCache.get(domain);
|
||||
if (cached && (Date.now() - cached.cachedAt) < DISCOVERY_TTL_MS) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const wellKnownUrl = `https://${domain}/.well-known/redlight`;
|
||||
const TIMEOUT_MS = 10_000; // 10 seconds
|
||||
try {
|
||||
// Since we test locally, allow http fallback if the request fails (optional but good for testing)
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(wellKnownUrl);
|
||||
response = await fetch(wellKnownUrl, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
||||
} catch (e) {
|
||||
if (e.message.includes('fetch') && wellKnownUrl.startsWith('https://localhost')) {
|
||||
response = await fetch(`http://${domain}/.well-known/redlight`);
|
||||
response = await fetch(`http://${domain}/.well-known/redlight`, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
||||
} else throw e;
|
||||
}
|
||||
|
||||
@@ -127,17 +129,17 @@ export async function discoverInstance(domain) {
|
||||
}
|
||||
|
||||
const baseUrl = `https://${domain}${data.federation_api || '/api/federation'}`;
|
||||
// Optionally handle local testing gracefully for baseUrl
|
||||
const result = {
|
||||
baseUrl: baseUrl.replace('https://localhost', 'http://localhost'),
|
||||
publicKey: data.public_key
|
||||
publicKey: data.public_key,
|
||||
cachedAt: Date.now(),
|
||||
};
|
||||
|
||||
discoveryCache.set(domain, result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`Federation discovery failed for ${domain}:`, error.message);
|
||||
throw new Error(`Could not discover Redlight instance at ${domain}`);
|
||||
throw new Error(`Could not discover Redlight instance at ${domain}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ export function initMailer() {
|
||||
port,
|
||||
secure: port === 465,
|
||||
auth: { user, pass },
|
||||
connectionTimeout: 10_000, // 10 s to establish TCP connection
|
||||
greetingTimeout: 10_000, // 10 s to receive SMTP greeting
|
||||
socketTimeout: 15_000, // 15 s of inactivity before abort
|
||||
});
|
||||
|
||||
console.log('✅ SMTP mailer configured');
|
||||
|
||||
@@ -100,6 +100,7 @@ router.post('/invite', authenticateToken, async (req, res) => {
|
||||
'X-Federation-Origin': getFederationDomain(),
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
signal: AbortSignal.timeout(15_000), // 15 second timeout
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -192,17 +193,17 @@ router.post('/receive', async (req, res) => {
|
||||
} catch { /* column may not exist on very old installs */ }
|
||||
}
|
||||
|
||||
// Send notification email (fire-and-forget, don't fail the request if mail fails)
|
||||
try {
|
||||
const appUrl = process.env.APP_URL || '';
|
||||
const inboxUrl = `${appUrl}/federation/inbox`;
|
||||
const appName = process.env.APP_NAME || 'Redlight';
|
||||
await sendFederationInviteEmail(
|
||||
targetUser.email, targetUser.name, from_user,
|
||||
room_name, message || null, inboxUrl, appName
|
||||
);
|
||||
} catch (mailErr) {
|
||||
// Send notification email (truly fire-and-forget – never blocks the response)
|
||||
if (targetUser.email) {
|
||||
const appUrl = process.env.APP_URL || '';
|
||||
const inboxUrl = `${appUrl}/federation/inbox`;
|
||||
const appName = process.env.APP_NAME || 'Redlight';
|
||||
sendFederationInviteEmail(
|
||||
targetUser.email, targetUser.name, from_user,
|
||||
room_name, message || null, inboxUrl, appName
|
||||
).catch(mailErr => {
|
||||
console.warn('Federation invite mail failed (non-fatal):', mailErr.message);
|
||||
});
|
||||
}
|
||||
|
||||
res.json({ success: true });
|
||||
|
||||
Reference in New Issue
Block a user