refactor: replace secure storage with app storage across components and hooks
This commit is contained in:
@@ -4,7 +4,7 @@ import { useAppEvent } from '@repo/core-events';
|
||||
import { useTranslation } from '@repo/core-i18n';
|
||||
import { Bookmark, Trash2 } from 'lucide-react';
|
||||
import { LAYOUT_EVENTS } from '../../../../../core/constants/events';
|
||||
import { secureIndexedDB, AppStorageKey } from '../../../../../core/storage/local';
|
||||
import { appDatabase, AppDatabaseKey } from '../../../../../core/storage/local';
|
||||
import { Link } from 'react-router-dom';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
@@ -25,7 +25,7 @@ export function BookmarkDrawer() {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const data = await secureIndexedDB.getItem<SavedPageItem[]>(AppStorageKey.BOOKMARK_PAGE);
|
||||
const data = await appDatabase.getItem<SavedPageItem[]>(AppDatabaseKey.BOOKMARK_PAGE);
|
||||
setItems(data || []);
|
||||
} catch (e) {
|
||||
console.error('Failed to load bookmark items', e);
|
||||
@@ -44,7 +44,7 @@ export function BookmarkDrawer() {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
const newItems = items.filter((item) => item.id !== id);
|
||||
await secureIndexedDB.setItem(AppStorageKey.BOOKMARK_PAGE, newItems);
|
||||
await appDatabase.setItem(AppDatabaseKey.BOOKMARK_PAGE, newItems);
|
||||
setItems(newItems);
|
||||
} catch (err) {
|
||||
console.error('Failed to remove bookmark item', err);
|
||||
@@ -53,7 +53,7 @@ export function BookmarkDrawer() {
|
||||
|
||||
const handleClearAll = async () => {
|
||||
try {
|
||||
await secureIndexedDB.removeItem(AppStorageKey.BOOKMARK_PAGE);
|
||||
await appDatabase.removeItem(AppDatabaseKey.BOOKMARK_PAGE);
|
||||
setItems([]);
|
||||
} catch (err) {
|
||||
console.error('Failed to clear bookmarks', err);
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Box,
|
||||
} from '@repo/ui/components';
|
||||
import { Globe, Bell, HelpCircle, Settings, User, Briefcase, LogOut, ChevronDown, Sun, Moon } from 'lucide-react';
|
||||
import { AppStorageKey, secureStorage } from '../../../../core/storage/local';
|
||||
import { AppStorageKey, appStorage } from '../../../../core/storage/local';
|
||||
import { useThemeStore } from '../../../../core/stores/theme.store';
|
||||
import { NotificationDropdown } from './notifications/notification-dropdown';
|
||||
import { Link } from 'react-router-dom';
|
||||
@@ -131,7 +131,7 @@ export default function HeaderLayout() {
|
||||
onChange={async (val) => {
|
||||
if (!val) return;
|
||||
await i18n.changeLanguage(val);
|
||||
await secureStorage.setItem(AppStorageKey.LANGUAGE, val);
|
||||
await appStorage.setItem(AppStorageKey.LANGUAGE, val);
|
||||
}}
|
||||
styles={{
|
||||
input: {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useAppEvent } from '@repo/core-events';
|
||||
import { useTranslation } from '@repo/core-i18n';
|
||||
import { History, Clock, Trash2 } from 'lucide-react';
|
||||
import { LAYOUT_EVENTS } from '../../../../../core/constants/events';
|
||||
import { secureIndexedDB, AppStorageKey } from '../../../../../core/storage/local';
|
||||
import { appDatabase, AppDatabaseKey } from '../../../../../core/storage/local';
|
||||
import { Link } from 'react-router-dom';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
@@ -38,9 +38,9 @@ export function HistoryDrawer() {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const data = await secureIndexedDB.getItem<SavedPageItem[]>(AppStorageKey.HISTORY_PAGE);
|
||||
const data = await appDatabase.getItem<SavedPageItem[]>(AppDatabaseKey.HISTORY_PAGE);
|
||||
const settings =
|
||||
(await secureIndexedDB.getItem<SystemSettings>(AppStorageKey.SYSTEM_SETTINGS)) || DEFAULT_SYSTEM_SETTINGS;
|
||||
(await appDatabase.getItem<SystemSettings>(AppDatabaseKey.SYSTEM_SETTINGS)) || DEFAULT_SYSTEM_SETTINGS;
|
||||
|
||||
const now = Date.now();
|
||||
const unitMs = {
|
||||
@@ -55,7 +55,7 @@ export function HistoryDrawer() {
|
||||
|
||||
// Save back if we purged some items
|
||||
if (data && validItems.length !== data.length) {
|
||||
await secureIndexedDB.setItem(AppStorageKey.HISTORY_PAGE, validItems);
|
||||
await appDatabase.setItem(AppDatabaseKey.HISTORY_PAGE, validItems);
|
||||
}
|
||||
|
||||
setItems(validItems);
|
||||
@@ -74,7 +74,7 @@ export function HistoryDrawer() {
|
||||
const handleClearRange = async (range: 'lastHour' | 'today' | 'allTime') => {
|
||||
try {
|
||||
if (range === 'allTime') {
|
||||
await secureIndexedDB.removeItem(AppStorageKey.HISTORY_PAGE);
|
||||
await appDatabase.removeItem(AppDatabaseKey.HISTORY_PAGE);
|
||||
setItems([]);
|
||||
return;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export function HistoryDrawer() {
|
||||
const cutoff = range === 'lastHour' ? now - 60 * 60 * 1000 : new Date().setHours(0, 0, 0, 0); // start of today
|
||||
|
||||
const newItems = items.filter((item) => item.timestamp < cutoff);
|
||||
await secureIndexedDB.setItem(AppStorageKey.HISTORY_PAGE, newItems);
|
||||
await appDatabase.setItem(AppDatabaseKey.HISTORY_PAGE, newItems);
|
||||
setItems(newItems);
|
||||
} catch (e) {
|
||||
console.error('Failed to clear history items', e);
|
||||
@@ -95,7 +95,7 @@ export function HistoryDrawer() {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
const newItems = items.filter((item) => item.id !== id);
|
||||
await secureIndexedDB.setItem(AppStorageKey.HISTORY_PAGE, newItems);
|
||||
await appDatabase.setItem(AppDatabaseKey.HISTORY_PAGE, newItems);
|
||||
setItems(newItems);
|
||||
} catch (err) {
|
||||
console.error('Failed to remove history item', err);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { secureIndexedDB, AppStorageKey } from '../../../../core/storage/local';
|
||||
import { appDatabase, AppDatabaseKey } from '../../../../core/storage/local';
|
||||
import type { SavedPageItem } from '../types/saved-page.types';
|
||||
import type { SystemSettings } from '../../system/setting/types/setting.types';
|
||||
import { DEFAULT_SYSTEM_SETTINGS } from '../../system/setting/types/setting.types';
|
||||
@@ -34,7 +34,7 @@ export async function initializeAndPurgeHistoryBackground() {
|
||||
if (isHistoryReady) return;
|
||||
|
||||
try {
|
||||
const data = (await secureIndexedDB.getItem<SavedPageItem[]>(AppStorageKey.HISTORY_PAGE)) || [];
|
||||
const data = (await appDatabase.getItem<SavedPageItem[]>(AppDatabaseKey.HISTORY_PAGE)) || [];
|
||||
|
||||
if (data.length === 0) {
|
||||
isHistoryReady = true;
|
||||
@@ -42,7 +42,7 @@ export async function initializeAndPurgeHistoryBackground() {
|
||||
}
|
||||
|
||||
const settings =
|
||||
(await secureIndexedDB.getItem<SystemSettings>(AppStorageKey.SYSTEM_SETTINGS)) || DEFAULT_SYSTEM_SETTINGS;
|
||||
(await appDatabase.getItem<SystemSettings>(AppDatabaseKey.SYSTEM_SETTINGS)) || DEFAULT_SYSTEM_SETTINGS;
|
||||
const now = Date.now();
|
||||
const unitMs: Record<string, number> = {
|
||||
seconds: 1000,
|
||||
@@ -58,7 +58,7 @@ export async function initializeAndPurgeHistoryBackground() {
|
||||
isHistoryReady = true;
|
||||
|
||||
if (validItems.length !== data.length) {
|
||||
await secureIndexedDB.setItem(AppStorageKey.HISTORY_PAGE, validItems);
|
||||
await appDatabase.setItem(AppDatabaseKey.HISTORY_PAGE, validItems);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[HistoryManager] Failed to init history', err);
|
||||
@@ -100,7 +100,7 @@ export function useHistoryTracker() {
|
||||
historyChannel?.postMessage(newItem);
|
||||
|
||||
// 3. Save to IndexedDB in the background (Asynchronous)
|
||||
secureIndexedDB.setItem(AppStorageKey.HISTORY_PAGE, memoryHistoryCache).catch((err) => {
|
||||
appDatabase.setItem(AppDatabaseKey.HISTORY_PAGE, memoryHistoryCache).catch((err) => {
|
||||
console.error('[HistoryManager] Failed to track history', err);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { FieldNumberInput, FieldSelect } from '@repo/ui/form';
|
||||
import { secureIndexedDB, AppStorageKey } from '../../../../../core/storage/local';
|
||||
import { appDatabase, AppDatabaseKey } from '../../../../../core/storage/local';
|
||||
import type { SystemSettings, RetentionUnit } from '../types/setting.types';
|
||||
import { DEFAULT_SYSTEM_SETTINGS } from '../types/setting.types';
|
||||
|
||||
@@ -40,7 +40,7 @@ export function HistorySetting({ onDirtyChange }: { onDirtyChange: (isDirty: boo
|
||||
useEffect(() => {
|
||||
const loadSettings = async () => {
|
||||
try {
|
||||
const settings = await secureIndexedDB.getItem<SystemSettings>(AppStorageKey.SYSTEM_SETTINGS);
|
||||
const settings = await appDatabase.getItem<SystemSettings>(AppDatabaseKey.SYSTEM_SETTINGS);
|
||||
if (settings) {
|
||||
reset({
|
||||
historyRetentionValue: settings.historyRetentionValue,
|
||||
@@ -61,7 +61,7 @@ export function HistorySetting({ onDirtyChange }: { onDirtyChange: (isDirty: boo
|
||||
|
||||
const handleReset = async () => {
|
||||
try {
|
||||
const settings = await secureIndexedDB.getItem<SystemSettings>(AppStorageKey.SYSTEM_SETTINGS);
|
||||
const settings = await appDatabase.getItem<SystemSettings>(AppDatabaseKey.SYSTEM_SETTINGS);
|
||||
if (settings) {
|
||||
reset({
|
||||
historyRetentionValue: settings.historyRetentionValue,
|
||||
@@ -82,13 +82,13 @@ export function HistorySetting({ onDirtyChange }: { onDirtyChange: (isDirty: boo
|
||||
setLoading(true);
|
||||
try {
|
||||
const currentSettings =
|
||||
(await secureIndexedDB.getItem<SystemSettings>(AppStorageKey.SYSTEM_SETTINGS)) || DEFAULT_SYSTEM_SETTINGS;
|
||||
(await appDatabase.getItem<SystemSettings>(AppDatabaseKey.SYSTEM_SETTINGS)) || DEFAULT_SYSTEM_SETTINGS;
|
||||
const newSettings = {
|
||||
...currentSettings,
|
||||
historyRetentionValue: data.historyRetentionValue,
|
||||
historyRetentionUnit: data.historyRetentionUnit as RetentionUnit,
|
||||
};
|
||||
await secureIndexedDB.setItem(AppStorageKey.SYSTEM_SETTINGS, newSettings);
|
||||
await appDatabase.setItem(AppDatabaseKey.SYSTEM_SETTINGS, newSettings);
|
||||
|
||||
reset(data); // Reset form to clear dirty state
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createHttpClient } from '@repo/core-api/http-client';
|
||||
import { faroAdapter } from '@repo/core-api/observability';
|
||||
import { ENV } from '../environment';
|
||||
import { AppStorageKey, secureStorage } from '../storage/local';
|
||||
import { AppStorageKey, appStorage } from '../storage/local';
|
||||
|
||||
/**
|
||||
* Enterprise HTTP client for `apps/web`.
|
||||
@@ -29,7 +29,7 @@ export const apiClient = createHttpClient(
|
||||
config.headers['ex-timezone-offset-minutes'] = new Date().getTimezoneOffset();
|
||||
config.headers['ex-timezone-offset-hours'] = Math.floor(new Date().getTimezoneOffset() / 60);
|
||||
|
||||
const language = await secureStorage.getItem<string>(AppStorageKey.LANGUAGE);
|
||||
const language = await appStorage.getItem<string>(AppStorageKey.LANGUAGE);
|
||||
config.headers['ex-language'] = language ?? ENV.DEFAULT_LANGUAGE;
|
||||
|
||||
const token = localStorage.getItem('access_token');
|
||||
|
||||
@@ -6,39 +6,47 @@ export const AppStorageKey = {
|
||||
THEME: 'app_theme',
|
||||
ACCESS_TOKEN: 'access_token',
|
||||
REFRESH_TOKEN: 'refresh_token',
|
||||
MOCK_DB_COMPANY_A: 'mock_db_company_a',
|
||||
} as const;
|
||||
|
||||
export type AppStorageKeyValue = (typeof AppStorageKey)[keyof typeof AppStorageKey];
|
||||
|
||||
export const AppDatabaseKey = {
|
||||
OFFLINE_DRAFT: 'offline_draft',
|
||||
SYSTEM_SETTINGS: 'system_settings',
|
||||
HISTORY_PAGE: 'history_page',
|
||||
BOOKMARK_PAGE: 'bookmark_page',
|
||||
} as const;
|
||||
|
||||
export type AppStorageKeyValue = (typeof AppStorageKey)[keyof typeof AppStorageKey];
|
||||
export type AppDatabaseKeyValue = (typeof AppDatabaseKey)[keyof typeof AppDatabaseKey];
|
||||
|
||||
export const ENCRYPTED_KEYS = new Set<AppStorageKeyValue>([
|
||||
export const APP_STORAGE_ENCRYPTED_KEYS = new Set<AppStorageKeyValue>([
|
||||
AppStorageKey.USER_PROFILE,
|
||||
AppStorageKey.ACCESS_TOKEN,
|
||||
AppStorageKey.REFRESH_TOKEN,
|
||||
]);
|
||||
|
||||
export const PLAIN_KEYS = new Set<AppStorageKeyValue>([
|
||||
export const APP_STORAGE_PLAIN_KEYS = new Set<AppStorageKeyValue>([
|
||||
AppStorageKey.LANGUAGE,
|
||||
AppStorageKey.THEME,
|
||||
AppStorageKey.MOCK_DB_COMPANY_A,
|
||||
AppStorageKey.OFFLINE_DRAFT,
|
||||
AppStorageKey.SYSTEM_SETTINGS,
|
||||
AppStorageKey.HISTORY_PAGE,
|
||||
AppStorageKey.BOOKMARK_PAGE,
|
||||
]);
|
||||
|
||||
export const secureStorage = createLocalStorage<AppStorageKeyValue>({
|
||||
encryptedKeys: ENCRYPTED_KEYS,
|
||||
plainTextKeys: PLAIN_KEYS,
|
||||
export const APP_DATABASE_ENCRYPTED_KEYS = new Set<AppDatabaseKeyValue>([]);
|
||||
|
||||
export const APP_DATABASE_PLAIN_KEYS = new Set<AppDatabaseKeyValue>([
|
||||
AppDatabaseKey.OFFLINE_DRAFT,
|
||||
AppDatabaseKey.SYSTEM_SETTINGS,
|
||||
AppDatabaseKey.HISTORY_PAGE,
|
||||
AppDatabaseKey.BOOKMARK_PAGE,
|
||||
]);
|
||||
|
||||
export const appStorage = createLocalStorage<AppStorageKeyValue>({
|
||||
encryptedKeys: APP_STORAGE_ENCRYPTED_KEYS,
|
||||
plainTextKeys: APP_STORAGE_PLAIN_KEYS,
|
||||
});
|
||||
|
||||
export const secureIndexedDB = createIndexedDB<AppStorageKeyValue>({
|
||||
export const appDatabase = createIndexedDB<AppDatabaseKeyValue>({
|
||||
dbName: 'e_apps_db',
|
||||
storeName: 'web_store',
|
||||
encryptedKeys: ENCRYPTED_KEYS,
|
||||
plainTextKeys: PLAIN_KEYS,
|
||||
encryptedKeys: APP_DATABASE_ENCRYPTED_KEYS,
|
||||
plainTextKeys: APP_DATABASE_PLAIN_KEYS,
|
||||
});
|
||||
|
||||
@@ -16,21 +16,21 @@ import './main.css';
|
||||
import { lazy, StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { setupI18n } from '@repo/core-i18n';
|
||||
import { secureStorage, AppStorageKey } from './core/storage/local';
|
||||
import { appStorage, AppStorageKey } from './core/storage/local';
|
||||
import { ENV } from './core/environment';
|
||||
|
||||
const App = lazy(() => import('./apps'));
|
||||
|
||||
async function bootstrap() {
|
||||
// Initialize i18next and load language from secureStorage
|
||||
// Initialize i18next and load language from appStorage
|
||||
await setupI18n(
|
||||
{
|
||||
storageAdapter: {
|
||||
getLanguage: async () => {
|
||||
return secureStorage.getItem<string>(AppStorageKey.LANGUAGE);
|
||||
return appStorage.getItem<string>(AppStorageKey.LANGUAGE);
|
||||
},
|
||||
setLanguage: async (lng: string) => {
|
||||
await secureStorage.setItem(AppStorageKey.LANGUAGE, lng);
|
||||
await appStorage.setItem(AppStorageKey.LANGUAGE, lng);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user