fix and improve qr linking

This commit is contained in:
Egor 2026-01-18 23:04:03 -08:00
parent 9af3c2afcf
commit 9f1a103368
7 changed files with 65 additions and 20 deletions

View file

@ -40,7 +40,7 @@ Download the latest APK from [GitHub Releases](https://github.com/lone-cloud/sup
curl -L -O https://raw.githubusercontent.com/lone-cloud/sup/master/docker-compose.yml
# Download .env.example (optional)
curl -L -O https://raw.githubusercontent.com/lone-cloud/sup/master/.env.example
curl -L -O https://raw.githubusercontent.com/lone-cloud/sup/master/server/.env.example
# Configure (optional)
cp .env.example .env

View file

@ -60,7 +60,11 @@ const server = Bun.serve({
},
[ROUTES.LINK_QR]: {
GET: handleLinkQR,
GET: () =>
handleLinkQR(async () => {
daemon?.kill();
daemon = await startDaemon();
}),
},
[ROUTES.LINK_STATUS]: {

View file

@ -1,3 +1,4 @@
import { existsSync, unlinkSync } from 'node:fs';
import { rm } from 'node:fs/promises';
import { DEVICE_NAME, VERBOSE } from '../constants/config';
import { SIGNAL_CLI, SIGNAL_CLI_DATA, SIGNAL_CLI_SOCKET } from '../constants/paths';
@ -10,6 +11,8 @@ logVerbose(`Running signal-cli from ${SIGNAL_CLI}`);
let account: string | null = null;
let currentLinkUri: string | null = null;
export const hasLinkUri = () => currentLinkUri !== null;
export async function initSignal({ accountOverride }: { accountOverride?: string }) {
if (accountOverride) {
account = accountOverride;
@ -49,15 +52,18 @@ export async function finishLink() {
throw new Error('No link in progress');
}
const uri = currentLinkUri;
currentLinkUri = null;
const result = await call(
'finishLink',
{
deviceLinkUri: currentLinkUri,
deviceLinkUri: uri,
deviceName: DEVICE_NAME,
},
account,
);
currentLinkUri = null;
logSuccess('✓ Device linked successfully');
return result;
}
@ -65,9 +71,15 @@ export async function unlinkDevice() {
account = null;
currentLinkUri = null;
try {
await rm(SIGNAL_CLI_DATA, { recursive: true, force: true });
} catch {}
if (existsSync(SIGNAL_CLI_DATA)) {
logWarn('⚠ Unlinking device and removing account data...');
try {
await rm(SIGNAL_CLI_DATA, { recursive: true, force: true });
} catch (error) {
logWarn('❌ Failed to remove account data directory:', error);
}
}
}
export async function createGroup(name: string, members: string[] = []) {
@ -120,6 +132,15 @@ export async function startDaemon() {
let authError = false;
let cleaned = false;
if (existsSync(SIGNAL_CLI_SOCKET)) {
try {
unlinkSync(SIGNAL_CLI_SOCKET);
logVerbose('Removed stale socket file');
} catch (error) {
logError('Failed to remove stale socket file:', error);
}
}
const proc = Bun.spawn([SIGNAL_CLI, 'daemon', '--socket', SIGNAL_CLI_SOCKET], {
stdout: 'pipe',
stderr: 'pipe',

View file

@ -3,6 +3,7 @@ import { ROUTES, TEMPLATES } from '../constants/server';
import {
finishLink,
generateLinkQR,
hasLinkUri,
hasValidAccount,
initSignal,
unlinkDevice,
@ -26,7 +27,13 @@ export const handleLink = async () => {
});
};
export const handleLinkQR = async () => {
export const handleLinkQR = async (restartDaemon: () => Promise<void>) => {
const linked = await hasValidAccount();
if (!linked) {
await unlinkDevice();
await restartDaemon();
}
const qrDataUrl = await generateLinkQR();
return new Response(qrDataUrl, {
@ -37,7 +44,7 @@ export const handleLinkQR = async () => {
export const handleLinkStatus = async () => {
let linked = await hasValidAccount();
if (!linked) {
if (!linked && hasLinkUri()) {
try {
await finishLink();
await initSignal({});

View file

@ -12,17 +12,27 @@
<p style="font-size: 1.1em;"><strong>Settings <span style="font-size: 1.5em;"></span> Linked Devices <span style="font-size: 1.5em;"></span> Link New Device</strong></p>
<div id="qr">Generating QR code...</div>
<script>
fetch('/link/qr').then(r => r.text()).then(qr => {
document.getElementById('qr').innerHTML = `<img src="${qr}" style="max-width: 300px;" />`;
const check = setInterval(() => {
fetch('/link/status').then(r => r.json()).then(data => {
if (data.linked) {
clearInterval(check);
location.reload();
}
});
}, 2000);
});
let refreshInterval;
function loadQR() {
fetch('/link/qr').then(r => r.text()).then(qr => {
document.getElementById('qr').innerHTML = `<img src="${qr}" style="max-width: 300px;" />`;
});
}
loadQR();
refreshInterval = setInterval(loadQR, 30000);
const check = setInterval(() => {
fetch('/link/status').then(r => r.json()).then(data => {
if (data.linked) {
clearInterval(check);
clearInterval(refreshInterval);
location.reload();
}
});
}, 2000);
</script>
</body>
</html>

View file

@ -29,6 +29,9 @@ export const call = (method: string, params: Record<string, unknown>, account: s
error(_socket, error) {
reject(error);
},
connectError(_socket, error) {
reject(error);
},
close() {
const isComplete = response.includes(MESSAGE_DELIMITER);
if (!isComplete) {