import type { AuthUser, RefreshStore } from '@pipu/core'; import * as SecureStore from 'expo-secure-store'; // Всё храним в SecureStore (Android Keystore): список серверов маленький, а refresh-токен — секрет. const SERVERS_KEY = 'pipu.servers'; const CURRENT_KEY = 'pipu.server'; export interface SavedServer { url: string; version: string; /** Последний вошедший логин — подставляется в форму входа. */ username?: string; } /** Ключи SecureStore — только [A-Za-z0-9._-]: адрес сервера превращаем в безопасный суффикс. */ function keyFor(url: string): string { let h = 0; for (const ch of url) h = (h * 31 + ch.charCodeAt(0)) >>> 0; return `pipu.rt.${h.toString(36)}`; } export async function loadServers(): Promise { try { const raw = await SecureStore.getItemAsync(SERVERS_KEY); return raw ? (JSON.parse(raw) as SavedServer[]) : []; } catch { return []; } } /** Сохраняет сервер наверх списка (последний использованный — первый). */ export async function rememberServer(server: SavedServer): Promise { const list = (await loadServers()).filter((s) => s.url !== server.url); await SecureStore.setItemAsync(SERVERS_KEY, JSON.stringify([server, ...list].slice(0, 10))); } export async function forgetServer(url: string): Promise { const list = (await loadServers()).filter((s) => s.url !== url); await SecureStore.setItemAsync(SERVERS_KEY, JSON.stringify(list)); await refreshStoreFor(url).clear(); if ((await currentServer()) === url) await SecureStore.deleteItemAsync(CURRENT_KEY); } export async function currentServer(): Promise { return SecureStore.getItemAsync(CURRENT_KEY); } export async function setCurrentServer(url: string | null): Promise { if (url) await SecureStore.setItemAsync(CURRENT_KEY, url); else await SecureStore.deleteItemAsync(CURRENT_KEY); } /** Refresh-токен сервера (у каждого сервера — свой) и последний пользователь — для запуска без сети. */ export function refreshStoreFor(url: string): RefreshStore { const key = keyFor(url); const userKey = key.replace('pipu.rt.', 'pipu.user.'); return { get: () => SecureStore.getItemAsync(key), set: (token) => SecureStore.setItemAsync(key, token), clear: async () => { await SecureStore.deleteItemAsync(key); await SecureStore.deleteItemAsync(userKey); }, getUser: async () => { try { const raw = await SecureStore.getItemAsync(userKey); return raw ? (JSON.parse(raw) as AuthUser) : null; } catch { return null; } }, setUser: (user) => SecureStore.setItemAsync(userKey, JSON.stringify(user)), }; } /** Короткий стабильный хэш строки — для имён файлов и ключей. */ export function shortHash(s: string): string { let h = 0; for (const ch of s) h = (h * 31 + ch.charCodeAt(0)) >>> 0; return h.toString(36); }