mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26:16 -08:00
fix(server-storage): merge user data on move, keep failed writes, sign-in route gate
- A merged playlist gains the browser document's favorites and history; Xtream values for one server entry are combined (lists unioned, maps merged, the server winning conflicts) and written once. - A failed user-data write stays queued and survives a re-read; the page-hide flush waits for a batch still travelling. - The start-up screen lets the sign-in route through whatever its check reports. - The backend refuses IPTVNATOR_ACCESS_TOKEN without server storage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
676e3adcd4
commit
fc32934ce3
17 files changed
+474
-90
No files matched your search
@@ -1,4 +1,7 @@
|
||||
import { readServerStorageConfig } from './storage-config';
|
||||
import {
|
||||
readServerStorageConfig,
|
||||
serverStorageConfigProblem,
|
||||
} from './storage-config';
|
||||
|
||||
describe('readServerStorageConfig', () => {
|
||||
it('keeps storage off and the API open by default', () => {
|
||||
@@ -42,4 +45,20 @@ describe('readServerStorageConfig', () => {
|
||||
.maxDocumentBytes
|
||||
).toBe(64 * 1024 * 1024);
|
||||
});
|
||||
|
||||
it('refuses an access token without server storage', () => {
|
||||
const problem = (env: Record<string, string>) =>
|
||||
serverStorageConfigProblem(readServerStorageConfig(env));
|
||||
|
||||
expect(problem({ IPTVNATOR_ACCESS_TOKEN: 's3cret' })).toContain(
|
||||
'IPTVNATOR_STORAGE=server'
|
||||
);
|
||||
expect(
|
||||
problem({
|
||||
IPTVNATOR_ACCESS_TOKEN: 's3cret',
|
||||
IPTVNATOR_STORAGE: 'server',
|
||||
})
|
||||
).toBeNull();
|
||||
expect(problem({})).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -10,6 +10,19 @@ export interface ServerStorageConfig {
|
||||
const DEFAULT_MAX_DOCUMENT_MB = 64;
|
||||
const BYTES_PER_MB = 1024 * 1024;
|
||||
|
||||
/**
|
||||
* A combination the browser app cannot use, as a start-up message, or
|
||||
* `null`. The PWA asks for the access token only in server storage mode,
|
||||
* so a token on a stateless backend would lock every browser out.
|
||||
*/
|
||||
export function serverStorageConfigProblem(
|
||||
config: ServerStorageConfig
|
||||
): string | null {
|
||||
return config.accessToken && !config.enabled
|
||||
? 'IPTVNATOR_ACCESS_TOKEN needs IPTVNATOR_STORAGE=server: the browser app asks for the token only when the server stores its playlists. Unset the token, or protect a stateless backend in your reverse proxy.'
|
||||
: null;
|
||||
}
|
||||
|
||||
export function readServerStorageConfig(
|
||||
env: Record<string, string | undefined> = process.env
|
||||
): ServerStorageConfig {
|
||||
|
||||
@@ -4,7 +4,10 @@ import {
|
||||
} from '@iptvnator/shared/host-health';
|
||||
import { closeGracefully } from './app/graceful-shutdown';
|
||||
import { createWebBackendApp } from './app/web-backend-app';
|
||||
import { readServerStorageConfig } from './app/storage/storage-config';
|
||||
import {
|
||||
readServerStorageConfig,
|
||||
serverStorageConfigProblem,
|
||||
} from './app/storage/storage-config';
|
||||
import {
|
||||
openServerStorage,
|
||||
type ServerStorage,
|
||||
@@ -20,6 +23,11 @@ async function start(): Promise<void> {
|
||||
}
|
||||
|
||||
const config = readServerStorageConfig();
|
||||
const problem = serverStorageConfigProblem(config);
|
||||
if (problem) {
|
||||
console.error(`[web-backend] ${problem}`);
|
||||
process.exit(1);
|
||||
}
|
||||
let storage: ServerStorage | null = null;
|
||||
if (config.enabled) {
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user