mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-12 07:36:42 +08:00
feat(server): entitlement primitive (#14964)
#### PR Dependency Tree * **PR #14964** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added entitlement resolution to validate licenses and derive plan, quotas, expiry and flags. * Introduced persistent quota/entitlement state for users and workspaces with legacy sync behavior. * Real-time quota-state operations and change events for monitoring usage. * **Chores** * Updated workspace dependencies to add cryptography/hash crates. * **Tests** * Added native entitlement tests covering validation, quantity handling, and signature/expiry cases. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/toeverything/AFFiNE/pull/14964) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import test from 'ava';
|
||||
|
||||
import { resolveEntitlementV1 } from '../native';
|
||||
|
||||
test('native entitlement wrapper maps schema errors to invalid argument', t => {
|
||||
const error = t.throws(() =>
|
||||
resolveEntitlementV1({
|
||||
deploymentType: 'local',
|
||||
targetType: 'workspace',
|
||||
now: '2026-05-14T00:00:00Z',
|
||||
})
|
||||
);
|
||||
|
||||
t.is((error as Error & { code?: string })?.code, 'InvalidArg');
|
||||
});
|
||||
|
||||
test('native entitlement wrapper maps unsafe JS quantity to invalid argument', t => {
|
||||
const base = {
|
||||
deploymentType: 'cloud',
|
||||
targetType: 'workspace',
|
||||
plan: 'team',
|
||||
now: '2026-05-14T00:00:00Z',
|
||||
} as const;
|
||||
|
||||
for (const quantity of [4294967297, 1.5, 100001]) {
|
||||
const error = t.throws(() => resolveEntitlementV1({ ...base, quantity }));
|
||||
|
||||
t.is(
|
||||
(error as Error & { code?: string })?.code,
|
||||
'InvalidArg',
|
||||
String(quantity)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('native entitlement wrapper does not trust forged signed payload buffers', t => {
|
||||
const resolved = resolveEntitlementV1({
|
||||
deploymentType: 'selfhosted',
|
||||
targetType: 'workspace',
|
||||
targetId: 'workspace-id',
|
||||
signedPayload: Buffer.from('not-a-valid-license'),
|
||||
publicKey: 'not-a-valid-public-key',
|
||||
licenseAesKey: 'not-a-valid-aes-key',
|
||||
now: '2026-05-14T00:00:00Z',
|
||||
});
|
||||
|
||||
t.false(resolved.valid);
|
||||
t.is(resolved.status, 'needs_reupload');
|
||||
t.is(resolved.plan, 'selfhost_free');
|
||||
});
|
||||
@@ -34,6 +34,8 @@ import serverNativeModule, {
|
||||
type RemoteAttachmentFetchResponse,
|
||||
type RemoteMimeTypeRequest,
|
||||
type RequestedModelMatchResponse,
|
||||
type ResolvedEntitlement,
|
||||
type ResolveEntitlementInput,
|
||||
type SafeFetchRequest,
|
||||
type SafeFetchResponse,
|
||||
type Tokenizer,
|
||||
@@ -51,6 +53,8 @@ export type {
|
||||
RemoteAttachmentFetchRequest,
|
||||
RemoteAttachmentFetchResponse,
|
||||
RemoteMimeTypeRequest,
|
||||
ResolvedEntitlement,
|
||||
ResolveEntitlementInput,
|
||||
SafeFetchRequest,
|
||||
SafeFetchResponse,
|
||||
};
|
||||
@@ -250,6 +254,10 @@ export const permissionActionRoleMatrixV1 = (): unknown =>
|
||||
export const permissionActionRoleMatrixV1Json =
|
||||
serverNativeModule.permissionActionRoleMatrixV1Json;
|
||||
|
||||
export const resolveEntitlementV1 = (
|
||||
input: ResolveEntitlementInput
|
||||
): ResolvedEntitlement => serverNativeModule.resolveEntitlementV1(input);
|
||||
|
||||
// MCP write tools exports
|
||||
export const createDocWithMarkdown = serverNativeModule.createDocWithMarkdown;
|
||||
export const updateDocWithMarkdown = serverNativeModule.updateDocWithMarkdown;
|
||||
|
||||
Reference in New Issue
Block a user