add postgres integration to save progress across devices
This commit is contained in:
109
src/lib/settings/sync.ts
Normal file
109
src/lib/settings/sync.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { derived, get, writable } from 'svelte/store';
|
||||
import { debounce, showSnackbar } from '$lib/util';
|
||||
import { currentProfile, profiles } from './settings';
|
||||
import { miscSettings } from './misc';
|
||||
import { volumes } from './volume-data';
|
||||
|
||||
export type SyncConfig = {
|
||||
key: string | null;
|
||||
auto: boolean;
|
||||
notifications: boolean;
|
||||
};
|
||||
|
||||
const stored = browser ? window.localStorage.getItem('syncConfig') : undefined;
|
||||
const initial: SyncConfig = stored ? JSON.parse(stored) : { key: null, auto: false, notifications: true };
|
||||
export const syncConfig = writable<SyncConfig>(initial);
|
||||
|
||||
syncConfig.subscribe((value) => {
|
||||
if (browser) {
|
||||
window.localStorage.setItem('syncConfig', JSON.stringify(value));
|
||||
}
|
||||
});
|
||||
|
||||
export const isSyncEnabled = derived(syncConfig, ($cfg) => Boolean($cfg.key));
|
||||
|
||||
async function callApi(method: 'GET' | 'POST', payload?: any) {
|
||||
if (!browser) return null;
|
||||
const url = method === 'GET' ? `/api/sync?key=${encodeURIComponent(get(syncConfig).key || '')}` : '/api/sync';
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: method === 'POST' ? JSON.stringify(payload) : undefined
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function pullAll() {
|
||||
const cfg = get(syncConfig);
|
||||
if (!cfg.key) throw new Error('No sync key configured');
|
||||
const data = await callApi('GET');
|
||||
if (!data) return;
|
||||
|
||||
if (data.profiles) {
|
||||
profiles.set(data.profiles);
|
||||
}
|
||||
if (data.current_profile) {
|
||||
currentProfile.set(data.current_profile);
|
||||
}
|
||||
if (data.misc_settings) {
|
||||
miscSettings.set(data.misc_settings);
|
||||
}
|
||||
if (data.volumes) {
|
||||
volumes.set(data.volumes);
|
||||
}
|
||||
if (cfg.notifications) {
|
||||
showSnackbar('Pulled from server');
|
||||
}
|
||||
}
|
||||
|
||||
export async function pushAll() {
|
||||
const cfg = get(syncConfig);
|
||||
if (!cfg.key) throw new Error('No sync key configured');
|
||||
|
||||
const payload = {
|
||||
key: cfg.key,
|
||||
profiles: get(profiles),
|
||||
current_profile: get(currentProfile),
|
||||
misc_settings: get(miscSettings),
|
||||
volumes: get(volumes)
|
||||
};
|
||||
await callApi('POST', payload);
|
||||
if (cfg.notifications) {
|
||||
showSnackbar('Pushed to server');
|
||||
}
|
||||
}
|
||||
|
||||
function setupAutoSync() {
|
||||
if (!browser) return;
|
||||
let unsubscribeFns: Array<() => void> = [];
|
||||
|
||||
function resubscribe() {
|
||||
unsubscribeFns.forEach((fn) => fn());
|
||||
unsubscribeFns = [];
|
||||
|
||||
if (!get(syncConfig).auto || !get(syncConfig).key) return;
|
||||
|
||||
const schedulePush = () => debounce(() => {
|
||||
pushAll().catch((error) => {
|
||||
console.error('Auto-sync failed:', error);
|
||||
});
|
||||
}, 300);
|
||||
|
||||
unsubscribeFns.push(profiles.subscribe(() => schedulePush()));
|
||||
unsubscribeFns.push(currentProfile.subscribe(() => schedulePush()));
|
||||
unsubscribeFns.push(miscSettings.subscribe(() => schedulePush()));
|
||||
unsubscribeFns.push(volumes.subscribe(() => schedulePush()));
|
||||
}
|
||||
|
||||
resubscribe();
|
||||
unsubscribeFns.push(syncConfig.subscribe(() => resubscribe()));
|
||||
}
|
||||
|
||||
if (browser) {
|
||||
setupAutoSync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user