Implement hashtag url storage for easy retry
This commit is contained in:
@@ -28,6 +28,11 @@ PORT=8090 ./build/immich-sync
|
|||||||
|
|
||||||
Open `http://localhost:8090`.
|
Open `http://localhost:8090`.
|
||||||
|
|
||||||
|
After both share URLs are valid, starting a job updates the browser fragment in the form
|
||||||
|
`#left=<encoded-share-url>&right=<encoded-share-url>`. Reloading or bookmarking that URL restores both fields and
|
||||||
|
automatically starts a new inspection. Passwords are intentionally excluded from the fragment and must be entered
|
||||||
|
again for protected shares.
|
||||||
|
|
||||||
## WebSocket protocol
|
## WebSocket protocol
|
||||||
|
|
||||||
All messages are JSON. Each WebSocket accepts exactly one complete job request and closes after its terminal response. A client that does not send a request within 10 seconds is disconnected. Passwords are accepted only in client messages and are never placed in URLs or echoed in status responses.
|
All messages are JSON. Each WebSocket accepts exactly one complete job request and closes after its terminal response. A client that does not send a request within 10 seconds is disconnected. Passwords are accepted only in client messages and are never placed in URLs or echoed in status responses.
|
||||||
|
|||||||
+40
@@ -21,6 +21,43 @@ let jobCompleted = false;
|
|||||||
let abortRequested = false;
|
let abortRequested = false;
|
||||||
let syncRunning = false;
|
let syncRunning = false;
|
||||||
|
|
||||||
|
function normalizedShareUrl(value) {
|
||||||
|
try {
|
||||||
|
const url = new URL(value.trim());
|
||||||
|
if (!['http:', 'https:'].includes(url.protocol) || !url.hostname || url.username || url.password) return undefined;
|
||||||
|
const marker = url.pathname.lastIndexOf('/share/');
|
||||||
|
if (marker < 0) return undefined;
|
||||||
|
const credential = url.pathname.slice(marker + 7).replace(/\/+$/, '');
|
||||||
|
if (!credential || credential.includes('/')) return undefined;
|
||||||
|
url.search = '';
|
||||||
|
url.hash = '';
|
||||||
|
return url.toString();
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateShareFragment() {
|
||||||
|
const left = normalizedShareUrl($('#leftUrl').value);
|
||||||
|
const right = normalizedShareUrl($('#rightUrl').value);
|
||||||
|
if (!left || !right) {
|
||||||
|
if (location.hash) history.replaceState(null, '', `${location.pathname}${location.search}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fragment = new URLSearchParams({ left, right });
|
||||||
|
history.replaceState(null, '', `${location.pathname}${location.search}#${fragment}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreShareFragment() {
|
||||||
|
const fragment = new URLSearchParams(location.hash.slice(1));
|
||||||
|
const left = normalizedShareUrl(fragment.get('left') || '');
|
||||||
|
const right = normalizedShareUrl(fragment.get('right') || '');
|
||||||
|
if (!left || !right) return false;
|
||||||
|
$('#leftUrl').value = left;
|
||||||
|
$('#rightUrl').value = right;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function openJob(request) {
|
function openJob(request) {
|
||||||
if (socket && socket.readyState !== WebSocket.CLOSED) {
|
if (socket && socket.readyState !== WebSocket.CLOSED) {
|
||||||
ui.message.textContent = 'Please wait for the current job to finish.';
|
ui.message.textContent = 'Please wait for the current job to finish.';
|
||||||
@@ -34,6 +71,7 @@ function openJob(request) {
|
|||||||
jobCompleted = false;
|
jobCompleted = false;
|
||||||
abortRequested = false;
|
abortRequested = false;
|
||||||
ui.inspect.disabled = true;
|
ui.inspect.disabled = true;
|
||||||
|
updateShareFragment();
|
||||||
setConnection('connecting', 'Connecting');
|
setConnection('connecting', 'Connecting');
|
||||||
|
|
||||||
jobSocket.addEventListener('open', () => {
|
jobSocket.addEventListener('open', () => {
|
||||||
@@ -242,6 +280,7 @@ function handle(message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui.inspect.addEventListener('click', inspect);
|
ui.inspect.addEventListener('click', inspect);
|
||||||
|
for (const input of [$('#leftUrl'), $('#rightUrl')]) input.addEventListener('input', updateShareFragment);
|
||||||
for (const input of $$('input')) input.addEventListener('keydown', (event) => {
|
for (const input of $$('input')) input.addEventListener('keydown', (event) => {
|
||||||
if (event.key === 'Enter') inspect();
|
if (event.key === 'Enter') inspect();
|
||||||
});
|
});
|
||||||
@@ -254,3 +293,4 @@ ui.abort.addEventListener('click', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setConnection('idle', 'Idle');
|
setConnection('idle', 'Idle');
|
||||||
|
if (restoreShareFragment()) inspect();
|
||||||
|
|||||||
Reference in New Issue
Block a user