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,
|
||||
} 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 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
|
||||
);
|
||||
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 excludeDomainsParam = url.searchParams.get('excludeDomains');
|
||||
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);
|
||||
if (!user) {
|
||||
@@ -42,7 +44,7 @@ export async function handleSync(request: Request, env: Env, userId: string): Pr
|
||||
}
|
||||
|
||||
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);
|
||||
if (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([
|
||||
storage.getAllCiphers(userId),
|
||||
storage.getAllFolders(userId),
|
||||
storage.getAllSends(userId),
|
||||
excludeSends ? Promise.resolve([]) : storage.getAllSends(userId),
|
||||
storage.getAttachmentsByUserId(userId),
|
||||
]);
|
||||
const accountKeys = buildAccountKeys(user);
|
||||
|
||||
+81
-42
@@ -169,6 +169,7 @@ export default function App() {
|
||||
const [decryptedFolders, setDecryptedFolders] = useState<VaultFolder[]>([]);
|
||||
const [decryptedCiphers, setDecryptedCiphers] = useState<Cipher[]>([]);
|
||||
const [decryptedSends, setDecryptedSends] = useState<Send[]>([]);
|
||||
const [vaultInitialDecryptDone, setVaultInitialDecryptDone] = useState(false);
|
||||
const sessionRef = useRef<SessionState | null>(initialBootstrap.session);
|
||||
const migratedPlainFolderIdsRef = useRef<Set<string>>(new Set());
|
||||
const silentRefreshVaultRef = useRef<() => Promise<void>>(async () => {});
|
||||
@@ -740,37 +741,38 @@ export default function App() {
|
||||
const sendsQuery = useQuery({
|
||||
queryKey: ['sends', session?.accessToken],
|
||||
queryFn: () => getSends(authedFetch),
|
||||
enabled: phase === 'app' && !!session?.symEncKey && !!session?.symMacKey,
|
||||
enabled: phase === 'app' && !!session?.symEncKey && !!session?.symMacKey && (vaultInitialDecryptDone || location === '/sends'),
|
||||
});
|
||||
const usersQuery = useQuery({
|
||||
queryKey: ['admin-users', session?.accessToken],
|
||||
queryFn: () => listAdminUsers(authedFetch),
|
||||
enabled: phase === 'app' && profile?.role === 'admin',
|
||||
enabled: phase === 'app' && profile?.role === 'admin' && vaultInitialDecryptDone,
|
||||
});
|
||||
const invitesQuery = useQuery({
|
||||
queryKey: ['admin-invites', session?.accessToken],
|
||||
queryFn: () => listAdminInvites(authedFetch),
|
||||
enabled: phase === 'app' && profile?.role === 'admin',
|
||||
enabled: phase === 'app' && profile?.role === 'admin' && vaultInitialDecryptDone,
|
||||
});
|
||||
const totpStatusQuery = useQuery({
|
||||
queryKey: ['totp-status', session?.accessToken],
|
||||
queryFn: () => getTotpStatus(authedFetch),
|
||||
enabled: phase === 'app' && !!session?.accessToken,
|
||||
enabled: phase === 'app' && !!session?.accessToken && vaultInitialDecryptDone,
|
||||
});
|
||||
const authorizedDevicesQuery = useQuery({
|
||||
queryKey: ['authorized-devices', session?.accessToken],
|
||||
queryFn: () => getAuthorizedDevices(authedFetch),
|
||||
enabled: phase === 'app' && !!session?.accessToken,
|
||||
enabled: phase === 'app' && !!session?.accessToken && vaultInitialDecryptDone,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (phase !== 'app' || !session?.accessToken || !session?.symEncKey || !session?.symMacKey) return;
|
||||
if (!vaultInitialDecryptDone) return;
|
||||
if (!profile?.role || profile.role !== 'admin') return;
|
||||
if (repairAttemptRef.current === session.accessToken) return;
|
||||
|
||||
repairAttemptRef.current = session.accessToken;
|
||||
void silentlyRepairBackupSettingsIfNeeded(session, profile);
|
||||
}, [phase, session?.accessToken, session?.symEncKey, session?.symMacKey, profile]);
|
||||
}, [phase, session?.accessToken, session?.symEncKey, session?.symMacKey, profile, vaultInitialDecryptDone]);
|
||||
|
||||
useEffect(() => {
|
||||
if (session?.accessToken) return;
|
||||
@@ -782,9 +784,10 @@ export default function App() {
|
||||
setDecryptedFolders([]);
|
||||
setDecryptedCiphers([]);
|
||||
setDecryptedSends([]);
|
||||
setVaultInitialDecryptDone(false);
|
||||
return;
|
||||
}
|
||||
if (!foldersQuery.data || !ciphersQuery.data || !sendsQuery.data) return;
|
||||
if (!foldersQuery.data || !ciphersQuery.data) return;
|
||||
|
||||
let active = true;
|
||||
(async () => {
|
||||
@@ -982,41 +985,76 @@ export default function App() {
|
||||
})
|
||||
);
|
||||
|
||||
const sends = await Promise.all(
|
||||
sendsQuery.data.map(async (send) => {
|
||||
const nextSend: Send = { ...send };
|
||||
try {
|
||||
if (send.key) {
|
||||
const sendKeyRaw = await decryptBw(send.key, encKey, macKey);
|
||||
const derived = await deriveSendKeyParts(sendKeyRaw);
|
||||
nextSend.decName = await decryptField(send.name || '', derived.enc, derived.mac);
|
||||
nextSend.decNotes = await decryptField(send.notes || '', derived.enc, derived.mac);
|
||||
nextSend.decText = await decryptField(send.text?.text || '', derived.enc, derived.mac);
|
||||
if (send.file?.fileName) {
|
||||
const decFileName = await decryptField(send.file.fileName, derived.enc, derived.mac);
|
||||
nextSend.file = {
|
||||
...(send.file || {}),
|
||||
fileName: decFileName || send.file.fileName,
|
||||
};
|
||||
}
|
||||
const shareKey = await buildSendShareKey(send.key, session.symEncKey!, session.symMacKey!);
|
||||
nextSend.decShareKey = shareKey;
|
||||
nextSend.shareUrl = buildPublicSendUrl(window.location.origin, send.accessId, shareKey);
|
||||
} else {
|
||||
nextSend.decName = '';
|
||||
nextSend.decNotes = '';
|
||||
nextSend.decText = '';
|
||||
}
|
||||
} catch {
|
||||
nextSend.decName = t('txt_decrypt_failed');
|
||||
}
|
||||
return nextSend;
|
||||
})
|
||||
);
|
||||
|
||||
if (!active) return;
|
||||
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 };
|
||||
try {
|
||||
if (send.key) {
|
||||
const sendKeyRaw = await decryptBw(send.key, encKey, macKey);
|
||||
const derived = await deriveSendKeyParts(sendKeyRaw);
|
||||
nextSend.decName = await decryptField(send.name || '', derived.enc, derived.mac);
|
||||
nextSend.decNotes = await decryptField(send.notes || '', derived.enc, derived.mac);
|
||||
nextSend.decText = await decryptField(send.text?.text || '', derived.enc, derived.mac);
|
||||
if (send.file?.fileName) {
|
||||
const decFileName = await decryptField(send.file.fileName, derived.enc, derived.mac);
|
||||
nextSend.file = {
|
||||
...(send.file || {}),
|
||||
fileName: decFileName || send.file.fileName,
|
||||
};
|
||||
}
|
||||
const shareKey = await buildSendShareKey(send.key, session.symEncKey!, session.symMacKey!);
|
||||
nextSend.decShareKey = shareKey;
|
||||
nextSend.shareUrl = buildPublicSendUrl(window.location.origin, send.accessId, shareKey);
|
||||
} else {
|
||||
nextSend.decName = '';
|
||||
nextSend.decNotes = '';
|
||||
nextSend.decText = '';
|
||||
}
|
||||
} catch {
|
||||
nextSend.decName = t('txt_decrypt_failed');
|
||||
}
|
||||
return nextSend;
|
||||
}));
|
||||
|
||||
if (!active) return;
|
||||
setDecryptedSends(sends);
|
||||
} catch (error) {
|
||||
if (!active) return;
|
||||
@@ -1027,7 +1065,7 @@ export default function App() {
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [session?.symEncKey, session?.symMacKey, foldersQuery.data, ciphersQuery.data, sendsQuery.data]);
|
||||
}, [session?.symEncKey, session?.symMacKey, sendsQuery.data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!session?.symEncKey || !session?.symMacKey || !foldersQuery.data?.length) return;
|
||||
@@ -1061,7 +1099,7 @@ export default function App() {
|
||||
silentRefreshVaultRef.current = refreshVaultSilently;
|
||||
|
||||
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 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({
|
||||
authedFetch,
|
||||
@@ -1227,6 +1265,7 @@ export default function App() {
|
||||
});
|
||||
|
||||
refreshAuthorizedDevicesRef.current = async () => {
|
||||
if (!vaultInitialDecryptDone) return;
|
||||
await authorizedDevicesQuery.refetch();
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useEffect } from 'preact/hooks';
|
||||
import { Link, Route, Switch } from 'wouter';
|
||||
import { ArrowUpDown, Cloud, LogOut, Settings as SettingsIcon, Shield, ShieldUser } from 'lucide-preact';
|
||||
import type { ImportAttachmentFile, ImportResultSummary } from '@/components/ImportPage';
|
||||
import VaultPage from '@/components/VaultPage';
|
||||
import type { AdminBackupImportResponse, AdminBackupRunResponse, AdminBackupSettings, RemoteBackupBrowserResponse } from '@/lib/api/backup';
|
||||
import type { CiphersImportPayload } from '@/lib/api/vault';
|
||||
import { t } from '@/lib/i18n';
|
||||
@@ -11,7 +12,6 @@ import type { ExportRequest } from '@/lib/export-formats';
|
||||
|
||||
const SendsPage = lazy(() => import('@/components/SendsPage'));
|
||||
const TotpCodesPage = lazy(() => import('@/components/TotpCodesPage'));
|
||||
const VaultPage = lazy(() => import('@/components/VaultPage'));
|
||||
const SettingsPage = lazy(() => import('@/components/SettingsPage'));
|
||||
const SecurityDevicesPage = lazy(() => import('@/components/SecurityDevicesPage'));
|
||||
const AdminPage = lazy(() => import('@/components/AdminPage'));
|
||||
@@ -181,38 +181,36 @@ export default function AppMainRoutes(props: AppMainRoutesProps) {
|
||||
</Suspense>
|
||||
</Route>
|
||||
<Route path="/vault">
|
||||
<Suspense fallback={<RouteContentFallback />}>
|
||||
<VaultPage
|
||||
ciphers={props.decryptedCiphers}
|
||||
folders={props.decryptedFolders}
|
||||
loading={props.ciphersLoading || props.foldersLoading}
|
||||
emailForReprompt={props.profile?.email || props.session?.email || ''}
|
||||
onRefresh={props.onRefreshVault}
|
||||
onCreate={props.onCreateVaultItem}
|
||||
onUpdate={props.onUpdateVaultItem}
|
||||
onDelete={props.onDeleteVaultItem}
|
||||
onArchive={props.onArchiveVaultItem}
|
||||
onUnarchive={props.onUnarchiveVaultItem}
|
||||
onBulkDelete={props.onBulkDeleteVaultItems}
|
||||
onBulkPermanentDelete={props.onBulkPermanentDeleteVaultItems}
|
||||
onBulkRestore={props.onBulkRestoreVaultItems}
|
||||
onBulkArchive={props.onBulkArchiveVaultItems}
|
||||
onBulkUnarchive={props.onBulkUnarchiveVaultItems}
|
||||
onBulkMove={props.onBulkMoveVaultItems}
|
||||
onVerifyMasterPassword={props.onVerifyMasterPassword}
|
||||
onNotify={props.onNotify}
|
||||
onCreateFolder={props.onCreateFolder}
|
||||
onRenameFolder={props.onRenameFolder}
|
||||
onDeleteFolder={props.onDeleteFolder}
|
||||
onBulkDeleteFolders={props.onBulkDeleteFolders}
|
||||
onDownloadAttachment={props.onDownloadVaultAttachment}
|
||||
downloadingAttachmentKey={props.downloadingAttachmentKey}
|
||||
attachmentDownloadPercent={props.attachmentDownloadPercent}
|
||||
uploadingAttachmentName={props.uploadingAttachmentName}
|
||||
attachmentUploadPercent={props.attachmentUploadPercent}
|
||||
mobileSidebarToggleKey={props.mobileSidebarToggleKey}
|
||||
/>
|
||||
</Suspense>
|
||||
<VaultPage
|
||||
ciphers={props.decryptedCiphers}
|
||||
folders={props.decryptedFolders}
|
||||
loading={props.ciphersLoading || props.foldersLoading}
|
||||
emailForReprompt={props.profile?.email || props.session?.email || ''}
|
||||
onRefresh={props.onRefreshVault}
|
||||
onCreate={props.onCreateVaultItem}
|
||||
onUpdate={props.onUpdateVaultItem}
|
||||
onDelete={props.onDeleteVaultItem}
|
||||
onArchive={props.onArchiveVaultItem}
|
||||
onUnarchive={props.onUnarchiveVaultItem}
|
||||
onBulkDelete={props.onBulkDeleteVaultItems}
|
||||
onBulkPermanentDelete={props.onBulkPermanentDeleteVaultItems}
|
||||
onBulkRestore={props.onBulkRestoreVaultItems}
|
||||
onBulkArchive={props.onBulkArchiveVaultItems}
|
||||
onBulkUnarchive={props.onBulkUnarchiveVaultItems}
|
||||
onBulkMove={props.onBulkMoveVaultItems}
|
||||
onVerifyMasterPassword={props.onVerifyMasterPassword}
|
||||
onNotify={props.onNotify}
|
||||
onCreateFolder={props.onCreateFolder}
|
||||
onRenameFolder={props.onRenameFolder}
|
||||
onDeleteFolder={props.onDeleteFolder}
|
||||
onBulkDeleteFolders={props.onBulkDeleteFolders}
|
||||
onDownloadAttachment={props.onDownloadVaultAttachment}
|
||||
downloadingAttachmentKey={props.downloadingAttachmentKey}
|
||||
attachmentDownloadPercent={props.attachmentDownloadPercent}
|
||||
uploadingAttachmentName={props.uploadingAttachmentName}
|
||||
attachmentUploadPercent={props.attachmentUploadPercent}
|
||||
mobileSidebarToggleKey={props.mobileSidebarToggleKey}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={props.settingsAccountRoute}>
|
||||
{props.profile && (
|
||||
|
||||
@@ -203,10 +203,9 @@ export default function VaultListPanel(props: VaultListPanelProps) {
|
||||
})
|
||||
);
|
||||
|
||||
const sortableCiphers = props.canReorder ? props.filteredCiphers : props.visibleCiphers;
|
||||
const virtualPadTop = props.canReorder ? 0 : props.virtualRange.padTop;
|
||||
const virtualPadBottom = props.canReorder ? 0 : props.virtualRange.padBottom;
|
||||
const activeDragCipher = activeDragId ? sortableCiphers.find((cipher) => cipher.id === activeDragId) || null : null;
|
||||
const sortableItems = props.filteredCiphers.map((cipher) => cipher.id);
|
||||
const renderedCiphers = props.visibleCiphers;
|
||||
const activeDragCipher = activeDragId ? props.filteredCiphers.find((cipher) => cipher.id === activeDragId) || null : null;
|
||||
|
||||
const handleDragStart = (event: DragStartEvent) => {
|
||||
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)}>
|
||||
{!!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}>
|
||||
<SortableContext items={sortableCiphers.map((cipher) => cipher.id)} strategy={verticalListSortingStrategy}>
|
||||
{sortableCiphers.map((cipher) => (
|
||||
<SortableContext items={sortableItems} strategy={verticalListSortingStrategy}>
|
||||
{renderedCiphers.map((cipher) => (
|
||||
<SortableCipherListItem
|
||||
key={cipher.id}
|
||||
cipher={cipher}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { base64ToBytes, bytesToBase64, decryptBw, decryptBwFileData, decryptStr, encryptBw, encryptBwFileData, hkdf, pbkdf2 } from '../crypto';
|
||||
import type { Send, SendDraft, SessionState } from '../types';
|
||||
import { chunkArray, createApiError, parseErrorMessage, parseJson, uploadDirectEncryptedPayload, type AuthedFetch } from './shared';
|
||||
import { loadVaultSyncSnapshot } from './vault-sync';
|
||||
|
||||
function toIsoDateFromDays(value: string, required: boolean): string | null {
|
||||
const raw = String(value || '').trim();
|
||||
@@ -62,8 +61,10 @@ function parseMaxAccessCountRaw(value: string): number | null {
|
||||
}
|
||||
|
||||
export async function getSends(authedFetch: AuthedFetch): Promise<Send[]> {
|
||||
const body = await loadVaultSyncSnapshot(authedFetch);
|
||||
return body.sends || [];
|
||||
const resp = await authedFetch('/api/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(
|
||||
|
||||
@@ -7,14 +7,14 @@ interface VaultSyncResponse {
|
||||
sends?: Send[];
|
||||
}
|
||||
|
||||
const pendingSyncRequests = new WeakMap<AuthedFetch, Promise<VaultSyncResponse>>();
|
||||
const pendingVaultCoreRequests = new WeakMap<AuthedFetch, Promise<VaultSyncResponse>>();
|
||||
|
||||
export async function loadVaultSyncSnapshot(authedFetch: AuthedFetch): Promise<VaultSyncResponse> {
|
||||
const existing = pendingSyncRequests.get(authedFetch);
|
||||
export async function loadVaultCoreSyncSnapshot(authedFetch: AuthedFetch): Promise<VaultSyncResponse> {
|
||||
const existing = pendingVaultCoreRequests.get(authedFetch);
|
||||
if (existing) return existing;
|
||||
|
||||
const request = (async () => {
|
||||
const resp = await authedFetch('/api/sync', {
|
||||
const resp = await authedFetch('/api/sync?excludeSends=true&excludeDomains=true', {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache',
|
||||
@@ -26,12 +26,12 @@ export async function loadVaultSyncSnapshot(authedFetch: AuthedFetch): Promise<V
|
||||
return body || {};
|
||||
})();
|
||||
|
||||
pendingSyncRequests.set(authedFetch, request);
|
||||
pendingVaultCoreRequests.set(authedFetch, request);
|
||||
try {
|
||||
return await request;
|
||||
} finally {
|
||||
if (pendingSyncRequests.get(authedFetch) === request) {
|
||||
pendingSyncRequests.delete(authedFetch);
|
||||
if (pendingVaultCoreRequests.get(authedFetch) === request) {
|
||||
pendingVaultCoreRequests.delete(authedFetch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ import {
|
||||
type AuthedFetch,
|
||||
} from './shared';
|
||||
import { readResponseBytesWithProgress } from '../download';
|
||||
import { loadVaultSyncSnapshot } from './vault-sync';
|
||||
import { loadVaultCoreSyncSnapshot } from './vault-sync';
|
||||
|
||||
export async function getFolders(authedFetch: AuthedFetch): Promise<Folder[]> {
|
||||
const body = await loadVaultSyncSnapshot(authedFetch);
|
||||
const body = await loadVaultCoreSyncSnapshot(authedFetch);
|
||||
return body.folders || [];
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function updateFolder(
|
||||
}
|
||||
|
||||
export async function getCiphers(authedFetch: AuthedFetch): Promise<Cipher[]> {
|
||||
const body = await loadVaultSyncSnapshot(authedFetch);
|
||||
const body = await loadVaultCoreSyncSnapshot(authedFetch);
|
||||
return body.ciphers || [];
|
||||
}
|
||||
|
||||
|
||||
@@ -42,10 +42,8 @@ export default defineConfig({
|
||||
normalized.includes('/src/components/ImportPage.tsx') ||
|
||||
normalized.includes('/src/lib/import-') ||
|
||||
normalized.includes('/src/lib/export-formats.ts') ||
|
||||
normalized.includes('/src/components/VaultPage.tsx') ||
|
||||
normalized.includes('/src/components/SendsPage.tsx') ||
|
||||
normalized.includes('/src/components/TotpCodesPage.tsx') ||
|
||||
normalized.includes('/src/components/vault/')
|
||||
normalized.includes('/src/components/TotpCodesPage.tsx')
|
||||
) {
|
||||
return 'workspace-suite';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user