mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
feat: enhance sync functionality by adding excludeSends option and refactor related API calls
This commit is contained in:
@@ -10,10 +10,10 @@ import {
|
|||||||
buildUserDecryptionOptions,
|
buildUserDecryptionOptions,
|
||||||
} from '../utils/user-decryption';
|
} from '../utils/user-decryption';
|
||||||
|
|
||||||
function buildSyncCacheRequest(request: Request, userId: string, revisionDate: string, excludeDomains: boolean): Request {
|
function buildSyncCacheRequest(request: Request, userId: string, revisionDate: string, excludeDomains: boolean, excludeSends: boolean): Request {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const cacheUrl = new URL(
|
const cacheUrl = new URL(
|
||||||
`/__nodewarden/cache/sync/${encodeURIComponent(userId)}/${encodeURIComponent(revisionDate)}/${excludeDomains ? '1' : '0'}`,
|
`/__nodewarden/cache/sync/${encodeURIComponent(userId)}/${encodeURIComponent(revisionDate)}/${excludeDomains ? '1' : '0'}/${excludeSends ? '1' : '0'}`,
|
||||||
url.origin
|
url.origin
|
||||||
);
|
);
|
||||||
return new Request(cacheUrl.toString(), { method: 'GET' });
|
return new Request(cacheUrl.toString(), { method: 'GET' });
|
||||||
@@ -35,6 +35,8 @@ export async function handleSync(request: Request, env: Env, userId: string): Pr
|
|||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const excludeDomainsParam = url.searchParams.get('excludeDomains');
|
const excludeDomainsParam = url.searchParams.get('excludeDomains');
|
||||||
const excludeDomains = excludeDomainsParam !== null && /^(1|true|yes)$/i.test(excludeDomainsParam);
|
const excludeDomains = excludeDomainsParam !== null && /^(1|true|yes)$/i.test(excludeDomainsParam);
|
||||||
|
const excludeSendsParam = url.searchParams.get('excludeSends');
|
||||||
|
const excludeSends = excludeSendsParam !== null && /^(1|true|yes)$/i.test(excludeSendsParam);
|
||||||
|
|
||||||
const user = await storage.getUserById(userId);
|
const user = await storage.getUserById(userId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@@ -42,7 +44,7 @@ export async function handleSync(request: Request, env: Env, userId: string): Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
const revisionDate = await storage.getRevisionDate(userId);
|
const revisionDate = await storage.getRevisionDate(userId);
|
||||||
const cacheRequest = buildSyncCacheRequest(request, userId, revisionDate, excludeDomains);
|
const cacheRequest = buildSyncCacheRequest(request, userId, revisionDate, excludeDomains, excludeSends);
|
||||||
const cachedResponse = await readSyncCache(cacheRequest);
|
const cachedResponse = await readSyncCache(cacheRequest);
|
||||||
if (cachedResponse) {
|
if (cachedResponse) {
|
||||||
return cachedResponse;
|
return cachedResponse;
|
||||||
@@ -51,7 +53,7 @@ export async function handleSync(request: Request, env: Env, userId: string): Pr
|
|||||||
const [ciphers, folders, sends, attachmentsByCipher] = await Promise.all([
|
const [ciphers, folders, sends, attachmentsByCipher] = await Promise.all([
|
||||||
storage.getAllCiphers(userId),
|
storage.getAllCiphers(userId),
|
||||||
storage.getAllFolders(userId),
|
storage.getAllFolders(userId),
|
||||||
storage.getAllSends(userId),
|
excludeSends ? Promise.resolve([]) : storage.getAllSends(userId),
|
||||||
storage.getAttachmentsByUserId(userId),
|
storage.getAttachmentsByUserId(userId),
|
||||||
]);
|
]);
|
||||||
const accountKeys = buildAccountKeys(user);
|
const accountKeys = buildAccountKeys(user);
|
||||||
|
|||||||
+55
-16
@@ -169,6 +169,7 @@ export default function App() {
|
|||||||
const [decryptedFolders, setDecryptedFolders] = useState<VaultFolder[]>([]);
|
const [decryptedFolders, setDecryptedFolders] = useState<VaultFolder[]>([]);
|
||||||
const [decryptedCiphers, setDecryptedCiphers] = useState<Cipher[]>([]);
|
const [decryptedCiphers, setDecryptedCiphers] = useState<Cipher[]>([]);
|
||||||
const [decryptedSends, setDecryptedSends] = useState<Send[]>([]);
|
const [decryptedSends, setDecryptedSends] = useState<Send[]>([]);
|
||||||
|
const [vaultInitialDecryptDone, setVaultInitialDecryptDone] = useState(false);
|
||||||
const sessionRef = useRef<SessionState | null>(initialBootstrap.session);
|
const sessionRef = useRef<SessionState | null>(initialBootstrap.session);
|
||||||
const migratedPlainFolderIdsRef = useRef<Set<string>>(new Set());
|
const migratedPlainFolderIdsRef = useRef<Set<string>>(new Set());
|
||||||
const silentRefreshVaultRef = useRef<() => Promise<void>>(async () => {});
|
const silentRefreshVaultRef = useRef<() => Promise<void>>(async () => {});
|
||||||
@@ -740,37 +741,38 @@ export default function App() {
|
|||||||
const sendsQuery = useQuery({
|
const sendsQuery = useQuery({
|
||||||
queryKey: ['sends', session?.accessToken],
|
queryKey: ['sends', session?.accessToken],
|
||||||
queryFn: () => getSends(authedFetch),
|
queryFn: () => getSends(authedFetch),
|
||||||
enabled: phase === 'app' && !!session?.symEncKey && !!session?.symMacKey,
|
enabled: phase === 'app' && !!session?.symEncKey && !!session?.symMacKey && (vaultInitialDecryptDone || location === '/sends'),
|
||||||
});
|
});
|
||||||
const usersQuery = useQuery({
|
const usersQuery = useQuery({
|
||||||
queryKey: ['admin-users', session?.accessToken],
|
queryKey: ['admin-users', session?.accessToken],
|
||||||
queryFn: () => listAdminUsers(authedFetch),
|
queryFn: () => listAdminUsers(authedFetch),
|
||||||
enabled: phase === 'app' && profile?.role === 'admin',
|
enabled: phase === 'app' && profile?.role === 'admin' && vaultInitialDecryptDone,
|
||||||
});
|
});
|
||||||
const invitesQuery = useQuery({
|
const invitesQuery = useQuery({
|
||||||
queryKey: ['admin-invites', session?.accessToken],
|
queryKey: ['admin-invites', session?.accessToken],
|
||||||
queryFn: () => listAdminInvites(authedFetch),
|
queryFn: () => listAdminInvites(authedFetch),
|
||||||
enabled: phase === 'app' && profile?.role === 'admin',
|
enabled: phase === 'app' && profile?.role === 'admin' && vaultInitialDecryptDone,
|
||||||
});
|
});
|
||||||
const totpStatusQuery = useQuery({
|
const totpStatusQuery = useQuery({
|
||||||
queryKey: ['totp-status', session?.accessToken],
|
queryKey: ['totp-status', session?.accessToken],
|
||||||
queryFn: () => getTotpStatus(authedFetch),
|
queryFn: () => getTotpStatus(authedFetch),
|
||||||
enabled: phase === 'app' && !!session?.accessToken,
|
enabled: phase === 'app' && !!session?.accessToken && vaultInitialDecryptDone,
|
||||||
});
|
});
|
||||||
const authorizedDevicesQuery = useQuery({
|
const authorizedDevicesQuery = useQuery({
|
||||||
queryKey: ['authorized-devices', session?.accessToken],
|
queryKey: ['authorized-devices', session?.accessToken],
|
||||||
queryFn: () => getAuthorizedDevices(authedFetch),
|
queryFn: () => getAuthorizedDevices(authedFetch),
|
||||||
enabled: phase === 'app' && !!session?.accessToken,
|
enabled: phase === 'app' && !!session?.accessToken && vaultInitialDecryptDone,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (phase !== 'app' || !session?.accessToken || !session?.symEncKey || !session?.symMacKey) return;
|
if (phase !== 'app' || !session?.accessToken || !session?.symEncKey || !session?.symMacKey) return;
|
||||||
|
if (!vaultInitialDecryptDone) return;
|
||||||
if (!profile?.role || profile.role !== 'admin') return;
|
if (!profile?.role || profile.role !== 'admin') return;
|
||||||
if (repairAttemptRef.current === session.accessToken) return;
|
if (repairAttemptRef.current === session.accessToken) return;
|
||||||
|
|
||||||
repairAttemptRef.current = session.accessToken;
|
repairAttemptRef.current = session.accessToken;
|
||||||
void silentlyRepairBackupSettingsIfNeeded(session, profile);
|
void silentlyRepairBackupSettingsIfNeeded(session, profile);
|
||||||
}, [phase, session?.accessToken, session?.symEncKey, session?.symMacKey, profile]);
|
}, [phase, session?.accessToken, session?.symEncKey, session?.symMacKey, profile, vaultInitialDecryptDone]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.accessToken) return;
|
if (session?.accessToken) return;
|
||||||
@@ -782,9 +784,10 @@ export default function App() {
|
|||||||
setDecryptedFolders([]);
|
setDecryptedFolders([]);
|
||||||
setDecryptedCiphers([]);
|
setDecryptedCiphers([]);
|
||||||
setDecryptedSends([]);
|
setDecryptedSends([]);
|
||||||
|
setVaultInitialDecryptDone(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!foldersQuery.data || !ciphersQuery.data || !sendsQuery.data) return;
|
if (!foldersQuery.data || !ciphersQuery.data) return;
|
||||||
|
|
||||||
let active = true;
|
let active = true;
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -982,8 +985,46 @@ export default function App() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const sends = await Promise.all(
|
if (!active) return;
|
||||||
sendsQuery.data.map(async (send) => {
|
setDecryptedFolders(folders);
|
||||||
|
setDecryptedCiphers(ciphers);
|
||||||
|
setVaultInitialDecryptDone(true);
|
||||||
|
} catch (error) {
|
||||||
|
if (!active) return;
|
||||||
|
pushToast('error', error instanceof Error ? error.message : t('txt_decrypt_failed_2'));
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
active = false;
|
||||||
|
};
|
||||||
|
}, [session?.symEncKey, session?.symMacKey, foldersQuery.data, ciphersQuery.data]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!session?.symEncKey || !session?.symMacKey) {
|
||||||
|
setDecryptedSends([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!sendsQuery.data) return;
|
||||||
|
|
||||||
|
let active = true;
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const encKey = base64ToBytes(session.symEncKey!);
|
||||||
|
const macKey = base64ToBytes(session.symMacKey!);
|
||||||
|
const decryptField = async (
|
||||||
|
value: string | null | undefined,
|
||||||
|
fieldEnc: Uint8Array = encKey,
|
||||||
|
fieldMac: Uint8Array = macKey
|
||||||
|
): Promise<string> => {
|
||||||
|
if (!value || typeof value !== 'string') return '';
|
||||||
|
try {
|
||||||
|
return await decryptStr(value, fieldEnc, fieldMac);
|
||||||
|
} catch {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const sends = await Promise.all(sendsQuery.data.map(async (send) => {
|
||||||
const nextSend: Send = { ...send };
|
const nextSend: Send = { ...send };
|
||||||
try {
|
try {
|
||||||
if (send.key) {
|
if (send.key) {
|
||||||
@@ -1011,12 +1052,9 @@ export default function App() {
|
|||||||
nextSend.decName = t('txt_decrypt_failed');
|
nextSend.decName = t('txt_decrypt_failed');
|
||||||
}
|
}
|
||||||
return nextSend;
|
return nextSend;
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
|
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
setDecryptedFolders(folders);
|
|
||||||
setDecryptedCiphers(ciphers);
|
|
||||||
setDecryptedSends(sends);
|
setDecryptedSends(sends);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
@@ -1027,7 +1065,7 @@ export default function App() {
|
|||||||
return () => {
|
return () => {
|
||||||
active = false;
|
active = false;
|
||||||
};
|
};
|
||||||
}, [session?.symEncKey, session?.symMacKey, foldersQuery.data, ciphersQuery.data, sendsQuery.data]);
|
}, [session?.symEncKey, session?.symMacKey, sendsQuery.data]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!session?.symEncKey || !session?.symMacKey || !foldersQuery.data?.length) return;
|
if (!session?.symEncKey || !session?.symMacKey || !foldersQuery.data?.length) return;
|
||||||
@@ -1061,7 +1099,7 @@ export default function App() {
|
|||||||
silentRefreshVaultRef.current = refreshVaultSilently;
|
silentRefreshVaultRef.current = refreshVaultSilently;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (phase !== 'app' || !session?.accessToken || !session?.symEncKey || !session?.symMacKey) return;
|
if (phase !== 'app' || !session?.accessToken || !session?.symEncKey || !session?.symMacKey || !vaultInitialDecryptDone) return;
|
||||||
|
|
||||||
let disposed = false;
|
let disposed = false;
|
||||||
let socket: WebSocket | null = null;
|
let socket: WebSocket | null = null;
|
||||||
@@ -1187,7 +1225,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [phase, session?.accessToken, session?.symEncKey, session?.symMacKey]);
|
}, [phase, session?.accessToken, session?.symEncKey, session?.symMacKey, vaultInitialDecryptDone]);
|
||||||
|
|
||||||
const vaultSendActions = useVaultSendActions({
|
const vaultSendActions = useVaultSendActions({
|
||||||
authedFetch,
|
authedFetch,
|
||||||
@@ -1227,6 +1265,7 @@ export default function App() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
refreshAuthorizedDevicesRef.current = async () => {
|
refreshAuthorizedDevicesRef.current = async () => {
|
||||||
|
if (!vaultInitialDecryptDone) return;
|
||||||
await authorizedDevicesQuery.refetch();
|
await authorizedDevicesQuery.refetch();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useEffect } from 'preact/hooks';
|
|||||||
import { Link, Route, Switch } from 'wouter';
|
import { Link, Route, Switch } from 'wouter';
|
||||||
import { ArrowUpDown, Cloud, LogOut, Settings as SettingsIcon, Shield, ShieldUser } from 'lucide-preact';
|
import { ArrowUpDown, Cloud, LogOut, Settings as SettingsIcon, Shield, ShieldUser } from 'lucide-preact';
|
||||||
import type { ImportAttachmentFile, ImportResultSummary } from '@/components/ImportPage';
|
import type { ImportAttachmentFile, ImportResultSummary } from '@/components/ImportPage';
|
||||||
|
import VaultPage from '@/components/VaultPage';
|
||||||
import type { AdminBackupImportResponse, AdminBackupRunResponse, AdminBackupSettings, RemoteBackupBrowserResponse } from '@/lib/api/backup';
|
import type { AdminBackupImportResponse, AdminBackupRunResponse, AdminBackupSettings, RemoteBackupBrowserResponse } from '@/lib/api/backup';
|
||||||
import type { CiphersImportPayload } from '@/lib/api/vault';
|
import type { CiphersImportPayload } from '@/lib/api/vault';
|
||||||
import { t } from '@/lib/i18n';
|
import { t } from '@/lib/i18n';
|
||||||
@@ -11,7 +12,6 @@ import type { ExportRequest } from '@/lib/export-formats';
|
|||||||
|
|
||||||
const SendsPage = lazy(() => import('@/components/SendsPage'));
|
const SendsPage = lazy(() => import('@/components/SendsPage'));
|
||||||
const TotpCodesPage = lazy(() => import('@/components/TotpCodesPage'));
|
const TotpCodesPage = lazy(() => import('@/components/TotpCodesPage'));
|
||||||
const VaultPage = lazy(() => import('@/components/VaultPage'));
|
|
||||||
const SettingsPage = lazy(() => import('@/components/SettingsPage'));
|
const SettingsPage = lazy(() => import('@/components/SettingsPage'));
|
||||||
const SecurityDevicesPage = lazy(() => import('@/components/SecurityDevicesPage'));
|
const SecurityDevicesPage = lazy(() => import('@/components/SecurityDevicesPage'));
|
||||||
const AdminPage = lazy(() => import('@/components/AdminPage'));
|
const AdminPage = lazy(() => import('@/components/AdminPage'));
|
||||||
@@ -181,7 +181,6 @@ export default function AppMainRoutes(props: AppMainRoutesProps) {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/vault">
|
<Route path="/vault">
|
||||||
<Suspense fallback={<RouteContentFallback />}>
|
|
||||||
<VaultPage
|
<VaultPage
|
||||||
ciphers={props.decryptedCiphers}
|
ciphers={props.decryptedCiphers}
|
||||||
folders={props.decryptedFolders}
|
folders={props.decryptedFolders}
|
||||||
@@ -212,7 +211,6 @@ export default function AppMainRoutes(props: AppMainRoutesProps) {
|
|||||||
attachmentUploadPercent={props.attachmentUploadPercent}
|
attachmentUploadPercent={props.attachmentUploadPercent}
|
||||||
mobileSidebarToggleKey={props.mobileSidebarToggleKey}
|
mobileSidebarToggleKey={props.mobileSidebarToggleKey}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={props.settingsAccountRoute}>
|
<Route path={props.settingsAccountRoute}>
|
||||||
{props.profile && (
|
{props.profile && (
|
||||||
|
|||||||
@@ -203,10 +203,9 @@ export default function VaultListPanel(props: VaultListPanelProps) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortableCiphers = props.canReorder ? props.filteredCiphers : props.visibleCiphers;
|
const sortableItems = props.filteredCiphers.map((cipher) => cipher.id);
|
||||||
const virtualPadTop = props.canReorder ? 0 : props.virtualRange.padTop;
|
const renderedCiphers = props.visibleCiphers;
|
||||||
const virtualPadBottom = props.canReorder ? 0 : props.virtualRange.padBottom;
|
const activeDragCipher = activeDragId ? props.filteredCiphers.find((cipher) => cipher.id === activeDragId) || null : null;
|
||||||
const activeDragCipher = activeDragId ? sortableCiphers.find((cipher) => cipher.id === activeDragId) || null : null;
|
|
||||||
|
|
||||||
const handleDragStart = (event: DragStartEvent) => {
|
const handleDragStart = (event: DragStartEvent) => {
|
||||||
setActiveDragId(String(event.active.id));
|
setActiveDragId(String(event.active.id));
|
||||||
@@ -357,10 +356,10 @@ export default function VaultListPanel(props: VaultListPanelProps) {
|
|||||||
|
|
||||||
<div className="list-panel" ref={props.listPanelRef} onScroll={(event) => props.onScroll((event.currentTarget as HTMLDivElement).scrollTop)}>
|
<div className="list-panel" ref={props.listPanelRef} onScroll={(event) => props.onScroll((event.currentTarget as HTMLDivElement).scrollTop)}>
|
||||||
{!!props.filteredCiphers.length && (
|
{!!props.filteredCiphers.length && (
|
||||||
<div style={{ paddingTop: `${virtualPadTop}px`, paddingBottom: `${virtualPadBottom}px` }}>
|
<div style={{ paddingTop: `${props.virtualRange.padTop}px`, paddingBottom: `${props.virtualRange.padBottom}px` }}>
|
||||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragStart={handleDragStart} onDragEnd={handleDragEnd} onDragCancel={handleDragCancel}>
|
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragStart={handleDragStart} onDragEnd={handleDragEnd} onDragCancel={handleDragCancel}>
|
||||||
<SortableContext items={sortableCiphers.map((cipher) => cipher.id)} strategy={verticalListSortingStrategy}>
|
<SortableContext items={sortableItems} strategy={verticalListSortingStrategy}>
|
||||||
{sortableCiphers.map((cipher) => (
|
{renderedCiphers.map((cipher) => (
|
||||||
<SortableCipherListItem
|
<SortableCipherListItem
|
||||||
key={cipher.id}
|
key={cipher.id}
|
||||||
cipher={cipher}
|
cipher={cipher}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { base64ToBytes, bytesToBase64, decryptBw, decryptBwFileData, decryptStr, encryptBw, encryptBwFileData, hkdf, pbkdf2 } from '../crypto';
|
import { base64ToBytes, bytesToBase64, decryptBw, decryptBwFileData, decryptStr, encryptBw, encryptBwFileData, hkdf, pbkdf2 } from '../crypto';
|
||||||
import type { Send, SendDraft, SessionState } from '../types';
|
import type { Send, SendDraft, SessionState } from '../types';
|
||||||
import { chunkArray, createApiError, parseErrorMessage, parseJson, uploadDirectEncryptedPayload, type AuthedFetch } from './shared';
|
import { chunkArray, createApiError, parseErrorMessage, parseJson, uploadDirectEncryptedPayload, type AuthedFetch } from './shared';
|
||||||
import { loadVaultSyncSnapshot } from './vault-sync';
|
|
||||||
|
|
||||||
function toIsoDateFromDays(value: string, required: boolean): string | null {
|
function toIsoDateFromDays(value: string, required: boolean): string | null {
|
||||||
const raw = String(value || '').trim();
|
const raw = String(value || '').trim();
|
||||||
@@ -62,8 +61,10 @@ function parseMaxAccessCountRaw(value: string): number | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getSends(authedFetch: AuthedFetch): Promise<Send[]> {
|
export async function getSends(authedFetch: AuthedFetch): Promise<Send[]> {
|
||||||
const body = await loadVaultSyncSnapshot(authedFetch);
|
const resp = await authedFetch('/api/sends');
|
||||||
return body.sends || [];
|
if (!resp.ok) throw new Error('Failed to load sends');
|
||||||
|
const body = await parseJson<{ data?: Send[] }>(resp);
|
||||||
|
return body?.data || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSend(
|
export async function createSend(
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ interface VaultSyncResponse {
|
|||||||
sends?: Send[];
|
sends?: Send[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const pendingSyncRequests = new WeakMap<AuthedFetch, Promise<VaultSyncResponse>>();
|
const pendingVaultCoreRequests = new WeakMap<AuthedFetch, Promise<VaultSyncResponse>>();
|
||||||
|
|
||||||
export async function loadVaultSyncSnapshot(authedFetch: AuthedFetch): Promise<VaultSyncResponse> {
|
export async function loadVaultCoreSyncSnapshot(authedFetch: AuthedFetch): Promise<VaultSyncResponse> {
|
||||||
const existing = pendingSyncRequests.get(authedFetch);
|
const existing = pendingVaultCoreRequests.get(authedFetch);
|
||||||
if (existing) return existing;
|
if (existing) return existing;
|
||||||
|
|
||||||
const request = (async () => {
|
const request = (async () => {
|
||||||
const resp = await authedFetch('/api/sync', {
|
const resp = await authedFetch('/api/sync?excludeSends=true&excludeDomains=true', {
|
||||||
cache: 'no-store',
|
cache: 'no-store',
|
||||||
headers: {
|
headers: {
|
||||||
'Cache-Control': 'no-cache',
|
'Cache-Control': 'no-cache',
|
||||||
@@ -26,12 +26,12 @@ export async function loadVaultSyncSnapshot(authedFetch: AuthedFetch): Promise<V
|
|||||||
return body || {};
|
return body || {};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
pendingSyncRequests.set(authedFetch, request);
|
pendingVaultCoreRequests.set(authedFetch, request);
|
||||||
try {
|
try {
|
||||||
return await request;
|
return await request;
|
||||||
} finally {
|
} finally {
|
||||||
if (pendingSyncRequests.get(authedFetch) === request) {
|
if (pendingVaultCoreRequests.get(authedFetch) === request) {
|
||||||
pendingSyncRequests.delete(authedFetch);
|
pendingVaultCoreRequests.delete(authedFetch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import {
|
|||||||
type AuthedFetch,
|
type AuthedFetch,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { readResponseBytesWithProgress } from '../download';
|
import { readResponseBytesWithProgress } from '../download';
|
||||||
import { loadVaultSyncSnapshot } from './vault-sync';
|
import { loadVaultCoreSyncSnapshot } from './vault-sync';
|
||||||
|
|
||||||
export async function getFolders(authedFetch: AuthedFetch): Promise<Folder[]> {
|
export async function getFolders(authedFetch: AuthedFetch): Promise<Folder[]> {
|
||||||
const body = await loadVaultSyncSnapshot(authedFetch);
|
const body = await loadVaultCoreSyncSnapshot(authedFetch);
|
||||||
return body.folders || [];
|
return body.folders || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ export async function updateFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCiphers(authedFetch: AuthedFetch): Promise<Cipher[]> {
|
export async function getCiphers(authedFetch: AuthedFetch): Promise<Cipher[]> {
|
||||||
const body = await loadVaultSyncSnapshot(authedFetch);
|
const body = await loadVaultCoreSyncSnapshot(authedFetch);
|
||||||
return body.ciphers || [];
|
return body.ciphers || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,8 @@ export default defineConfig({
|
|||||||
normalized.includes('/src/components/ImportPage.tsx') ||
|
normalized.includes('/src/components/ImportPage.tsx') ||
|
||||||
normalized.includes('/src/lib/import-') ||
|
normalized.includes('/src/lib/import-') ||
|
||||||
normalized.includes('/src/lib/export-formats.ts') ||
|
normalized.includes('/src/lib/export-formats.ts') ||
|
||||||
normalized.includes('/src/components/VaultPage.tsx') ||
|
|
||||||
normalized.includes('/src/components/SendsPage.tsx') ||
|
normalized.includes('/src/components/SendsPage.tsx') ||
|
||||||
normalized.includes('/src/components/TotpCodesPage.tsx') ||
|
normalized.includes('/src/components/TotpCodesPage.tsx')
|
||||||
normalized.includes('/src/components/vault/')
|
|
||||||
) {
|
) {
|
||||||
return 'workspace-suite';
|
return 'workspace-suite';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user