mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 14:16:16 -08:00
fix(server-storage): bind the token to its instance; adopted edits yield to newer changes
The access token is stored with the instance id it was given for, and a page reads it only for its own instance: a tab still open on the old backend after BACKEND_URL changed no longer sends the new server's token there. The API client hands every 401 to reportAccessDenied(), which now also sends a page without a token for its instance to sign-in, while one that holds its token back until the instance is checked keeps it. A closed tab's adopted edits carry `yields`: replayed, they change only what is still as that tab saw it, so they never replace progress this tab or another device saved since. The flag survives the journal. The sync spec's fake server and memory storage move to a test-helpers file to keep the spec under its line limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
61c3f844d6
commit
f9e5ee97d7
20 files changed
+426
-243
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { readServerAccessToken } from '@iptvnator/services';
|
||||
import {
|
||||
SERVER_STORAGE_CONFIG,
|
||||
ServerApiError,
|
||||
@@ -60,6 +61,9 @@ describe('ServerSignInComponent', () => {
|
||||
await component.submit(new Event('submit'));
|
||||
|
||||
expect(localStorage.getItem(TOKEN_KEY)).toBe('right');
|
||||
// Only for the instance it was given for.
|
||||
expect(readServerAccessToken('i-1')).toBe('right');
|
||||
expect(readServerAccessToken('i-2')).toBeNull();
|
||||
expect(open).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export class ServerSignInComponent {
|
||||
// Every request and the reload read the token from storage, and the
|
||||
// next start keeps it only for the server it remembers.
|
||||
if (
|
||||
!writeServerAccessToken(token) ||
|
||||
!writeServerAccessToken(token, this.config.instance.instanceId) ||
|
||||
!rememberServer(this.config.baseUrl, this.config.instance)
|
||||
) {
|
||||
clearServerAccessToken();
|
||||
|
||||
@@ -21,8 +21,9 @@ import {
|
||||
isStalkerProbeTimeout,
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
SERVER_ACCESS_TOKEN_STORAGE_KEY,
|
||||
clearServerAccessToken,
|
||||
ServerStorageStatusService,
|
||||
writeServerAccessToken,
|
||||
} from '@iptvnator/services';
|
||||
import { PwaService } from './pwa.service';
|
||||
|
||||
@@ -284,7 +285,7 @@ describe('PwaService', () => {
|
||||
});
|
||||
|
||||
it('sends a stored server token only while server mode runs', async () => {
|
||||
localStorage.setItem(SERVER_ACCESS_TOKEN_STORAGE_KEY, 'kept-token');
|
||||
writeServerAccessToken('kept-token', 'i');
|
||||
const originalFetch = globalThis.fetch;
|
||||
const fetchMock = jest.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
@@ -335,7 +336,7 @@ describe('PwaService', () => {
|
||||
});
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
localStorage.removeItem(SERVER_ACCESS_TOKEN_STORAGE_KEY);
|
||||
clearServerAccessToken();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user