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 curl -L -O https://raw.githubusercontent.com/lone-cloud/sup/master/docker-compose.yml
# Download .env.example (optional) # 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) # Configure (optional)
cp .env.example .env cp .env.example .env

View file

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

View file

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

View file

@ -3,6 +3,7 @@ import { ROUTES, TEMPLATES } from '../constants/server';
import { import {
finishLink, finishLink,
generateLinkQR, generateLinkQR,
hasLinkUri,
hasValidAccount, hasValidAccount,
initSignal, initSignal,
unlinkDevice, 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(); const qrDataUrl = await generateLinkQR();
return new Response(qrDataUrl, { return new Response(qrDataUrl, {
@ -37,7 +44,7 @@ export const handleLinkQR = async () => {
export const handleLinkStatus = async () => { export const handleLinkStatus = async () => {
let linked = await hasValidAccount(); let linked = await hasValidAccount();
if (!linked) { if (!linked && hasLinkUri()) {
try { try {
await finishLink(); await finishLink();
await initSignal({}); 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> <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> <div id="qr">Generating QR code...</div>
<script> <script>
fetch('/link/qr').then(r => r.text()).then(qr => { let refreshInterval;
document.getElementById('qr').innerHTML = `<img src="${qr}" style="max-width: 300px;" />`;
const check = setInterval(() => { function loadQR() {
fetch('/link/status').then(r => r.json()).then(data => { fetch('/link/qr').then(r => r.text()).then(qr => {
if (data.linked) { document.getElementById('qr').innerHTML = `<img src="${qr}" style="max-width: 300px;" />`;
clearInterval(check); });
location.reload(); }
}
}); loadQR();
}, 2000);
}); 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> </script>
</body> </body>
</html> </html>

View file

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