mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 06:47:02 +08:00
feat(workspace): add blob and storage limit (#5535)
close TOV-343 AFF-508 TOV-461 TOV-460 TOV-419 Add `isOverCapacity ` status to detect if blob usage exceeds limits. Add `onCapacityChange` and `onBlobSet` to monitor if the storage or blob exceeds the capacity limit. Global modals `LocalQuotaModal` and `CloudQuotaModal` have been added, with the upload size of the blob being limited within the modal components. The notification component has been adjusted, now you can pass in `action` click events and `actionLabel` .
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { Slot } from '@blocksuite/global/utils';
|
||||
import { difference } from 'lodash-es';
|
||||
|
||||
import { BlobStorageOverCapacity } from './error';
|
||||
|
||||
const logger = new DebugLogger('affine:blob-engine');
|
||||
|
||||
export interface BlobStatus {
|
||||
isStorageOverCapacity: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* # BlobEngine
|
||||
*
|
||||
@@ -12,6 +19,19 @@ const logger = new DebugLogger('affine:blob-engine');
|
||||
*/
|
||||
export class BlobEngine {
|
||||
private abort: AbortController | null = null;
|
||||
private _status: BlobStatus = { isStorageOverCapacity: false };
|
||||
onStatusChange = new Slot<BlobStatus>();
|
||||
singleBlobSizeLimit: number = 100 * 1024 * 1024;
|
||||
onAbortLargeBlob = new Slot<Blob>();
|
||||
|
||||
private set status(s: BlobStatus) {
|
||||
logger.debug('status change', s);
|
||||
this._status = s;
|
||||
this.onStatusChange.emit(s);
|
||||
}
|
||||
get status() {
|
||||
return this._status;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly local: BlobStorage,
|
||||
@@ -53,7 +73,7 @@ export class BlobEngine {
|
||||
}
|
||||
|
||||
async sync() {
|
||||
if (this.local.readonly) {
|
||||
if (this.local.readonly || this._status.isStorageOverCapacity) {
|
||||
return;
|
||||
}
|
||||
logger.debug('start syncing blob...');
|
||||
@@ -78,6 +98,11 @@ export class BlobEngine {
|
||||
await remote.set(key, data);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof BlobStorageOverCapacity) {
|
||||
this.status = {
|
||||
isStorageOverCapacity: true,
|
||||
};
|
||||
}
|
||||
logger.error(
|
||||
`error when sync ${key} from [${this.local.name}] to [${remote.name}]`,
|
||||
err
|
||||
@@ -122,6 +147,12 @@ export class BlobEngine {
|
||||
throw new Error('local peer is readonly');
|
||||
}
|
||||
|
||||
if (value.size > this.singleBlobSizeLimit) {
|
||||
this.onAbortLargeBlob.emit(value);
|
||||
logger.error('blob over limit, abort set');
|
||||
return key;
|
||||
}
|
||||
|
||||
// await upload to the local peer
|
||||
await this.local.set(key, value);
|
||||
|
||||
@@ -131,7 +162,7 @@ export class BlobEngine {
|
||||
.filter(r => !r.readonly)
|
||||
.map(peer =>
|
||||
peer.set(key, value).catch(err => {
|
||||
logger.error('error when upload to peer', err);
|
||||
logger.error('Error when uploading to peer', err);
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
5
packages/common/workspace/src/engine/error.ts
Normal file
5
packages/common/workspace/src/engine/error.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export class BlobStorageOverCapacity extends Error {
|
||||
constructor(public originError?: any) {
|
||||
super('Blob storage over capacity.');
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@ import { Slot } from '@blocksuite/global/utils';
|
||||
|
||||
import { throwIfAborted } from '../utils/throw-if-aborted';
|
||||
import type { AwarenessProvider } from './awareness';
|
||||
import type { BlobEngine } from './blob';
|
||||
import type { BlobEngine, BlobStatus } from './blob';
|
||||
import type { SyncEngine, SyncEngineStatus } from './sync';
|
||||
|
||||
export interface WorkspaceEngineStatus {
|
||||
sync: SyncEngineStatus;
|
||||
blob: BlobStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,10 +35,18 @@ export class WorkspaceEngine {
|
||||
) {
|
||||
this._status = {
|
||||
sync: sync.status,
|
||||
blob: blob.status,
|
||||
};
|
||||
sync.onStatusChange.on(status => {
|
||||
this.status = {
|
||||
sync: status,
|
||||
blob: blob.status,
|
||||
};
|
||||
});
|
||||
blob.onStatusChange.on(status => {
|
||||
this.status = {
|
||||
sync: sync.status,
|
||||
blob: status,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -71,4 +80,5 @@ export class WorkspaceEngine {
|
||||
|
||||
export * from './awareness';
|
||||
export * from './blob';
|
||||
export * from './error';
|
||||
export * from './sync';
|
||||
|
||||
Reference in New Issue
Block a user