mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
0a001bebcc
- Introduced a new DomainRulesPage component for managing custom and global equivalent domains. - Updated AppMainRoutes to include a route for domain rules. - Added API functions to fetch and save domain rules. - Enhanced localization with new strings for domain rules in multiple languages. - Updated styles for the new domain rules interface and ensured responsiveness. - Added types for domain rules in the TypeScript definitions.
145 lines
5.1 KiB
TypeScript
145 lines
5.1 KiB
TypeScript
import { Env, SyncResponse, CipherResponse, FolderResponse, ProfileResponse } from '../types';
|
|
import { StorageService } from '../services/storage';
|
|
import { errorResponse } from '../utils/response';
|
|
import { cipherToResponse, isCipherResponseSyncCompatible } from './ciphers';
|
|
import { sendToResponse } from './sends';
|
|
import { LIMITS } from '../config/limits';
|
|
import {
|
|
buildAccountKeys,
|
|
buildUserDecryptionCompat,
|
|
buildUserDecryptionOptions,
|
|
} from '../utils/user-decryption';
|
|
import { buildDomainsResponse } from '../services/domain-rules';
|
|
|
|
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'}/${excludeSends ? '1' : '0'}`,
|
|
url.origin
|
|
);
|
|
return new Request(cacheUrl.toString(), { method: 'GET' });
|
|
}
|
|
|
|
async function readSyncCache(cacheRequest: Request): Promise<Response | null> {
|
|
const hit = await caches.default.match(cacheRequest);
|
|
if (!hit) return null;
|
|
return new Response(hit.body, hit);
|
|
}
|
|
|
|
async function writeSyncCache(cacheRequest: Request, response: Response): Promise<void> {
|
|
await caches.default.put(cacheRequest, response.clone());
|
|
}
|
|
|
|
// GET /api/sync
|
|
export async function handleSync(request: Request, env: Env, userId: string): Promise<Response> {
|
|
const storage = new StorageService(env.DB);
|
|
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) {
|
|
return errorResponse('User not found', 404);
|
|
}
|
|
|
|
const revisionDate = await storage.getRevisionDate(userId);
|
|
const cacheRequest = buildSyncCacheRequest(request, userId, revisionDate, excludeDomains, excludeSends);
|
|
const cachedResponse = await readSyncCache(cacheRequest);
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
|
|
const [ciphers, folders, sends, attachmentsByCipher, domainSettings] = await Promise.all([
|
|
storage.getAllCiphers(userId),
|
|
storage.getAllFolders(userId),
|
|
excludeSends ? Promise.resolve([]) : storage.getAllSends(userId),
|
|
storage.getAttachmentsByUserId(userId),
|
|
excludeDomains ? Promise.resolve(null) : storage.getUserDomainSettings(userId),
|
|
]);
|
|
const accountKeys = buildAccountKeys(user);
|
|
const userDecryptionOptions = buildUserDecryptionOptions(user);
|
|
|
|
const profile: ProfileResponse = {
|
|
id: user.id,
|
|
name: user.name,
|
|
email: user.email,
|
|
emailVerified: true,
|
|
premium: true,
|
|
premiumFromOrganization: false,
|
|
usesKeyConnector: false,
|
|
masterPasswordHint: user.masterPasswordHint,
|
|
culture: 'en-US',
|
|
twoFactorEnabled: !!user.totpSecret,
|
|
key: user.key,
|
|
privateKey: user.privateKey,
|
|
accountKeys,
|
|
securityStamp: user.securityStamp || user.id,
|
|
organizations: [],
|
|
providers: [],
|
|
providerOrganizations: [],
|
|
forcePasswordReset: false,
|
|
avatarColor: null,
|
|
creationDate: user.createdAt,
|
|
verifyDevices: user.verifyDevices,
|
|
object: 'profile',
|
|
};
|
|
|
|
const cipherResponses: CipherResponse[] = [];
|
|
for (const cipher of ciphers) {
|
|
const response = cipherToResponse(cipher, attachmentsByCipher.get(cipher.id) || []);
|
|
if (isCipherResponseSyncCompatible(response)) {
|
|
cipherResponses.push(response);
|
|
}
|
|
}
|
|
|
|
const folderResponses: FolderResponse[] = [];
|
|
for (const folder of folders) {
|
|
folderResponses.push({
|
|
id: folder.id,
|
|
name: folder.name,
|
|
revisionDate: folder.updatedAt,
|
|
creationDate: folder.createdAt,
|
|
object: 'folder',
|
|
});
|
|
}
|
|
|
|
const sendResponses = sends.map(sendToResponse);
|
|
const syncResponse: SyncResponse = {
|
|
profile,
|
|
folders: folderResponses,
|
|
collections: [],
|
|
ciphers: cipherResponses,
|
|
domains: excludeDomains
|
|
? null
|
|
: buildDomainsResponse(
|
|
domainSettings?.equivalentDomains || [],
|
|
domainSettings?.customEquivalentDomains || [],
|
|
domainSettings?.excludedGlobalEquivalentDomains || [],
|
|
{ omitExcludedGlobals: true }
|
|
),
|
|
policies: [],
|
|
sends: sendResponses,
|
|
UserDecryption: {
|
|
MasterPasswordUnlock: userDecryptionOptions.MasterPasswordUnlock,
|
|
TrustedDeviceOption: null,
|
|
KeyConnectorOption: null,
|
|
Object: 'userDecryption',
|
|
},
|
|
UserDecryptionOptions: userDecryptionOptions,
|
|
userDecryption: buildUserDecryptionCompat(user) as SyncResponse['userDecryption'],
|
|
object: 'sync',
|
|
};
|
|
|
|
const response = new Response(JSON.stringify(syncResponse), {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cache-Control': `private, max-age=${Math.max(1, Math.floor(LIMITS.cache.syncResponseTtlMs / 1000))}`,
|
|
},
|
|
});
|
|
await writeSyncCache(cacheRequest, response);
|
|
return response;
|
|
}
|