Reconnection when signal server is not active and minor changes

This commit is contained in:
2026-03-18 20:45:00 +01:00
parent eb987ac672
commit 141de64767
15 changed files with 229 additions and 27 deletions

View File

@@ -4,6 +4,10 @@ function buildOrigin(protocol: string, host: string): string {
return `${protocol}://${host}`.replace(/\/+$/, '');
}
function originFromUrl(url: URL): string {
return buildOrigin(url.protocol.replace(':', ''), url.host);
}
export function getRequestOrigin(request: Request): string {
const forwardedProtoHeader = request.get('x-forwarded-proto');
const forwardedHostHeader = request.get('x-forwarded-host');
@@ -15,18 +19,24 @@ export function getRequestOrigin(request: Request): string {
export function deriveWebAppOrigin(signalOrigin: string): string {
const url = new URL(signalOrigin);
const host = url.host;
if (host === 'signal.toju.app') {
if (url.hostname === 'signal.toju.app' && !url.port) {
return 'https://web.toju.app';
}
if (host.startsWith('signal.')) {
return buildOrigin(url.protocol.replace(':', ''), host.replace(/^signal\./, 'web.'));
if (url.hostname.startsWith('signal.')) {
url.hostname = url.hostname.replace(/^signal\./, 'web.');
if (url.port === '3001') {
url.port = '4200';
}
return originFromUrl(url);
}
if (['localhost:3001', '127.0.0.1:3001'].includes(host)) {
return buildOrigin(url.protocol.replace(':', ''), host.replace(/:3001$/, ':4200'));
if (url.port === '3001') {
url.port = '4200';
return originFromUrl(url);
}
return 'https://web.toju.app';

View File

@@ -14,6 +14,7 @@ import {
buildSignalingUrl,
createServerInvite,
joinServerWithAccess,
leaveServerUser,
passwordHashForInput,
ServerAccessError,
kickServerUser,
@@ -341,6 +342,24 @@ router.post('/:id/moderation/unban', async (req, res) => {
res.json({ ok: true });
});
router.post('/:id/leave', async (req, res) => {
const { id: serverId } = req.params;
const { userId } = req.body;
const server = await getServerById(serverId);
if (!server) {
return res.status(404).json({ error: 'Server not found', errorCode: 'SERVER_NOT_FOUND' });
}
if (!userId) {
return res.status(400).json({ error: 'Missing userId', errorCode: 'MISSING_USER' });
}
await leaveServerUser(serverId, String(userId));
res.json({ ok: true });
});
router.post('/:id/heartbeat', async (req, res) => {
const { id } = req.params;
const { currentUsers } = req.body;

View File

@@ -63,6 +63,10 @@ function normalizePassword(password?: string | null): string | null {
return normalized.length > 0 ? normalized : null;
}
function isServerOwner(server: ServerEntity, userId: string): boolean {
return server.ownerId === userId;
}
export function hashServerPassword(password: string): string {
return crypto.createHash('sha256').update(password)
.digest('hex');
@@ -231,6 +235,18 @@ export async function joinServerWithAccess(options: {
throw new ServerAccessError(403, 'BANNED', 'Banned users cannot join this server');
}
if (isServerOwner(server, options.userId)) {
const existingMembership = await findServerMembership(server.id, options.userId);
await ensureServerMembership(server.id, options.userId);
return {
joinedBefore: !!existingMembership,
server: rowToServer(server),
via: 'membership'
};
}
if (options.inviteId) {
const inviteBundle = await getActiveServerInvite(options.inviteId);
@@ -305,6 +321,11 @@ export async function authorizeWebSocketJoin(serverId: string, userId: string):
reason: 'BANNED' };
}
if (isServerOwner(server, userId)) {
await ensureServerMembership(serverId, userId);
return { allowed: true };
}
const membership = await findServerMembership(serverId, userId);
if (membership) {
@@ -327,6 +348,10 @@ export async function kickServerUser(serverId: string, userId: string): Promise<
await removeServerMembership(serverId, userId);
}
export async function leaveServerUser(serverId: string, userId: string): Promise<void> {
await removeServerMembership(serverId, userId);
}
export async function banServerUser(options: BanServerUserOptions): Promise<ServerBanEntity> {
await removeServerMembership(options.serverId, options.userId);