Chore: Remove Old Directory (Prepare for Patches)

This commit is contained in:
Youwes09
2026-06-02 20:04:01 -05:00
parent 18027baee1
commit db8a984270
223 changed files with 595 additions and 34696 deletions
+69 -59
View File
@@ -1,88 +1,98 @@
const VAULT_KEY = "moku-credential-vault";
const SALT_ITERATIONS = 200_000;
const KEY_USAGE: KeyUsage[] = ["encrypt", "decrypt"];
import { platformService } from '$lib/platform-service'
const VAULT_STORE_KEY = 'moku-vault'
const SALT_ITERATIONS = 200_000
const KEY_USAGE: KeyUsage[] = ['encrypt', 'decrypt']
export interface VaultPayload {
refreshToken?: string;
basicUser?: string;
basicPass?: string;
authMode: "UI_LOGIN" | "BASIC_AUTH" | "NONE";
refreshToken?: string
basicUser?: string
basicPass?: string
authMode: 'UI_LOGIN' | 'BASIC_AUTH' | 'NONE'
}
interface StoredVault {
salt: string;
iv: string;
data: string;
salt: string
iv: string
data: string
}
function toB64(buf: ArrayBuffer): string {
return btoa(String.fromCharCode(...new Uint8Array(buf)));
return btoa(String.fromCharCode(...new Uint8Array(buf)))
}
function fromB64(s: string): Uint8Array {
return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
return Uint8Array.from(atob(s), c => c.charCodeAt(0))
}
async function deriveKey(pin: string, salt: Uint8Array): Promise<CryptoKey> {
const enc = new TextEncoder();
const keyMat = await crypto.subtle.importKey("raw", enc.encode(pin), "PBKDF2", false, ["deriveKey"]);
const enc = new TextEncoder()
const keyMat = await crypto.subtle.importKey('raw', enc.encode(pin), 'PBKDF2', false, ['deriveKey'])
return crypto.subtle.deriveKey(
{ name: "PBKDF2", salt, iterations: SALT_ITERATIONS, hash: "SHA-256" },
{ name: 'PBKDF2', salt, iterations: SALT_ITERATIONS, hash: 'SHA-256' },
keyMat,
{ name: "AES-GCM", length: 256 },
{ name: 'AES-GCM', length: 256 },
false,
KEY_USAGE,
);
)
}
export function vaultExists(): boolean {
return !!localStorage.getItem(VAULT_KEY);
}
export async function lockVault(pin: string, payload: VaultPayload): Promise<void> {
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const key = await deriveKey(pin, salt);
const enc = new TextEncoder();
const cipher = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
enc.encode(JSON.stringify(payload)),
);
localStorage.setItem(VAULT_KEY, JSON.stringify({
salt: toB64(salt),
iv: toB64(iv),
data: toB64(cipher),
} satisfies StoredVault));
}
export async function unlockVault(pin: string): Promise<VaultPayload | null> {
const raw = localStorage.getItem(VAULT_KEY);
if (!raw) return null;
async function readRaw(): Promise<StoredVault | null> {
try {
const stored = JSON.parse(raw) as StoredVault;
const key = await deriveKey(pin, fromB64(stored.salt));
const plain = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv: fromB64(stored.iv) },
key,
fromB64(stored.data),
);
return JSON.parse(new TextDecoder().decode(plain)) as VaultPayload;
const raw = await platformService.getCredential(VAULT_STORE_KEY)
return raw ? JSON.parse(raw) as StoredVault : null
} catch {
return null;
return null
}
}
export function clearVault(): void {
localStorage.removeItem(VAULT_KEY);
async function writeRaw(vault: StoredVault): Promise<void> {
await platformService.storeCredential(VAULT_STORE_KEY, JSON.stringify(vault))
}
export async function vaultExists(): Promise<boolean> {
return (await readRaw()) !== null
}
export async function lockVault(pin: string, payload: VaultPayload): Promise<void> {
const salt = crypto.getRandomValues(new Uint8Array(16))
const iv = crypto.getRandomValues(new Uint8Array(12))
const key = await deriveKey(pin, salt)
const enc = new TextEncoder()
const cipher = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
enc.encode(JSON.stringify(payload)),
)
await writeRaw({ salt: toB64(salt), iv: toB64(iv), data: toB64(cipher) })
}
export async function unlockVault(pin: string): Promise<VaultPayload | null> {
const stored = await readRaw()
if (!stored) return null
try {
const key = await deriveKey(pin, fromB64(stored.salt))
const plain = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: fromB64(stored.iv) },
key,
fromB64(stored.data),
)
return JSON.parse(new TextDecoder().decode(plain)) as VaultPayload
} catch {
return null
}
}
export async function clearVault(): Promise<void> {
await platformService.storeCredential(VAULT_STORE_KEY, '')
}
export async function rekeyVault(oldPin: string, newPin: string): Promise<boolean> {
const payload = await unlockVault(oldPin);
if (!payload) return false;
await lockVault(newPin, payload);
return true;
const payload = await unlockVault(oldPin)
if (!payload) return false
await lockVault(newPin, payload)
return true
}
+21 -4
View File
@@ -1,5 +1,22 @@
export { loadAllStores, persistSettings, persistLibrary, persistUpdates } from "./persist";
export type { PersistedData } from "./persist";
export {
loadSettings, saveSettings,
loadLibrary, saveLibrary,
loadUpdates, saveUpdates,
loadBackups, saveBackups,
} from './persist'
export { vaultExists, lockVault, unlockVault, clearVault, rekeyVault } from "./credentialVault";
export type { VaultPayload } from "./credentialVault";
export type {
PersistedSettings,
PersistedLibrary,
PersistedUpdates,
} from './persist'
export {
vaultExists,
lockVault,
unlockVault,
clearVault,
rekeyVault,
} from './credentialVault'
export type { VaultPayload } from './credentialVault'
+49 -49
View File
@@ -1,6 +1,6 @@
import { platformService } from '$lib/platform-service'
import type { ReadSession } from '$lib/types/history'
import type { BookmarkEntry, MarkerEntry } from '$lib/types/history'
import type { ReadSession } from '$lib/types/history'
import type { BookmarkEntry, MarkerEntry } from '$lib/types/history'
const STORE_VERSION = 2
@@ -22,10 +22,6 @@ export interface PersistedUpdates {
acknowledgedUpdateIds: number[]
}
export interface PersistedBackups {
backupList: { url: string; name: string }[]
}
function migrateLibrary(raw: unknown, fromVersion: number): PersistedLibrary {
const data = (raw ?? {}) as Record<string, unknown>
@@ -36,27 +32,25 @@ function migrateLibrary(raw: unknown, fromVersion: number): PersistedLibrary {
pageNumber?: number; readAt: number
}>
const sessions: ReadSession[] = oldHistory.map(e => ({
id: crypto.randomUUID(),
mangaId: e.mangaId,
mangaTitle: e.mangaTitle,
thumbnailUrl: e.thumbnailUrl,
startChapterId: e.chapterId,
startChapterName: e.chapterName,
endChapterId: e.chapterId,
endChapterName: e.chapterName,
startPage: 1,
endPage: e.pageNumber ?? 1,
startedAt: e.readAt,
endedAt: e.readAt,
durationMs: 0,
chaptersSpanned: 1,
}))
return {
sessions,
bookmarks: (data.bookmarks ?? []) as BookmarkEntry[],
markers: (data.markers ?? []) as MarkerEntry[],
sessions: oldHistory.map(e => ({
id: crypto.randomUUID(),
mangaId: e.mangaId,
mangaTitle: e.mangaTitle,
thumbnailUrl: e.thumbnailUrl,
startChapterId: e.chapterId,
startChapterName: e.chapterName,
endChapterId: e.chapterId,
endChapterName: e.chapterName,
startPage: 1,
endPage: e.pageNumber ?? 1,
startedAt: e.readAt,
endedAt: e.readAt,
durationMs: 0,
chaptersSpanned: 1,
})),
bookmarks: (data.bookmarks ?? []) as BookmarkEntry[],
markers: (data.markers ?? []) as MarkerEntry[],
dailyReadCounts: (data.dailyReadCounts ?? {}) as Record<string, number>,
}
}
@@ -69,19 +63,30 @@ function migrateLibrary(raw: unknown, fromVersion: number): PersistedLibrary {
}
}
function evacuateLocalStorage(key: string): unknown | null {
if (typeof window === 'undefined') return null
try {
const raw = localStorage.getItem(key)
if (!raw) return null
const parsed = JSON.parse(raw)
localStorage.removeItem(key)
return parsed
} catch {
return null
}
}
export async function loadSettings(): Promise<PersistedSettings> {
const raw = await platformService.loadStore('settings')
const raw = await platformService.loadStore('settings')
const data = (raw ?? {}) as Record<string, unknown>
const legacyRaw = typeof window !== 'undefined' ? localStorage.getItem('moku_settings') : null
if (legacyRaw && !data.settings) {
try {
const legacySettings = JSON.parse(legacyRaw)
localStorage.removeItem('moku_settings')
const result: PersistedSettings = { storeVersion: STORE_VERSION, settings: legacySettings }
if (!data.settings) {
const legacy = evacuateLocalStorage('moku_settings')
if (legacy) {
const result: PersistedSettings = { storeVersion: STORE_VERSION, settings: legacy }
await saveSettings(result)
return result
} catch {}
}
}
return {
@@ -99,15 +104,13 @@ export async function loadLibrary(): Promise<PersistedLibrary> {
const data = (raw ?? {}) as Record<string, unknown>
const version = (data.storeVersion as number) ?? 1
const legacyRaw = typeof window !== 'undefined' ? localStorage.getItem('moku-store') : null
if (legacyRaw && !(data.sessions || data.history)) {
try {
const legacy = JSON.parse(legacyRaw)
if (!data.sessions && !data.history) {
const legacy = evacuateLocalStorage('moku-store')
if (legacy) {
const migrated = migrateLibrary(legacy, 1)
localStorage.removeItem('moku-store')
await saveLibrary(migrated)
return migrated
} catch {}
}
}
return migrateLibrary(raw, version)
@@ -136,15 +139,12 @@ export async function loadBackups(): Promise<{ url: string; name: string }[]> {
const data = (raw ?? {}) as Record<string, unknown>
if (!data.backupList) {
try {
const legacyRaw = typeof window !== 'undefined' ? localStorage.getItem('moku_backups') : null
if (legacyRaw) {
const list = JSON.parse(legacyRaw) as { url: string; name: string }[]
localStorage.removeItem('moku_backups')
await saveBackups(list)
return list
}
} catch {}
const legacy = evacuateLocalStorage('moku_backups')
if (legacy) {
const list = legacy as { url: string; name: string }[]
await saveBackups(list)
return list
}
return []
}