feat(server): add blocked state to workspace docs (#10585)
close CLOUD-162
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "workspace_pages" ADD COLUMN "blocked" BOOLEAN NOT NULL DEFAULT false;
|
||||||
@@ -124,6 +124,8 @@ model WorkspaceDoc {
|
|||||||
defaultRole Int @default(30) @db.SmallInt
|
defaultRole Int @default(30) @db.SmallInt
|
||||||
// Page/Edgeless
|
// Page/Edgeless
|
||||||
mode Int @default(0) @db.SmallInt
|
mode Int @default(0) @db.SmallInt
|
||||||
|
// Whether the doc is blocked
|
||||||
|
blocked Boolean @default(false)
|
||||||
|
|
||||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
|||||||
@@ -439,6 +439,12 @@ export const USER_FRIENDLY_ERRORS = {
|
|||||||
message: ({ docId, action }) =>
|
message: ({ docId, action }) =>
|
||||||
`You do not have permission to perform ${action} action on doc ${docId}.`,
|
`You do not have permission to perform ${action} action on doc ${docId}.`,
|
||||||
},
|
},
|
||||||
|
doc_update_blocked: {
|
||||||
|
type: 'action_forbidden',
|
||||||
|
args: { spaceId: 'string', docId: 'string' },
|
||||||
|
message: ({ spaceId, docId }) =>
|
||||||
|
`Doc ${docId} under Space ${spaceId} is blocked from updating.`,
|
||||||
|
},
|
||||||
version_rejected: {
|
version_rejected: {
|
||||||
type: 'action_forbidden',
|
type: 'action_forbidden',
|
||||||
args: { version: 'string', serverVersion: 'string' },
|
args: { version: 'string', serverVersion: 'string' },
|
||||||
|
|||||||
@@ -334,6 +334,17 @@ export class DocActionDenied extends UserFriendlyError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
|
class DocUpdateBlockedDataType {
|
||||||
|
@Field() spaceId!: string
|
||||||
|
@Field() docId!: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DocUpdateBlocked extends UserFriendlyError {
|
||||||
|
constructor(args: DocUpdateBlockedDataType, message?: string | ((args: DocUpdateBlockedDataType) => string)) {
|
||||||
|
super('action_forbidden', 'doc_update_blocked', message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ObjectType()
|
||||||
class VersionRejectedDataType {
|
class VersionRejectedDataType {
|
||||||
@Field() version!: string
|
@Field() version!: string
|
||||||
@Field() serverVersion!: string
|
@Field() serverVersion!: string
|
||||||
@@ -871,6 +882,7 @@ export enum ErrorNames {
|
|||||||
CAN_NOT_REVOKE_YOURSELF,
|
CAN_NOT_REVOKE_YOURSELF,
|
||||||
DOC_NOT_FOUND,
|
DOC_NOT_FOUND,
|
||||||
DOC_ACTION_DENIED,
|
DOC_ACTION_DENIED,
|
||||||
|
DOC_UPDATE_BLOCKED,
|
||||||
VERSION_REJECTED,
|
VERSION_REJECTED,
|
||||||
INVALID_HISTORY_TIMESTAMP,
|
INVALID_HISTORY_TIMESTAMP,
|
||||||
DOC_HISTORY_NOT_FOUND,
|
DOC_HISTORY_NOT_FOUND,
|
||||||
@@ -942,5 +954,5 @@ registerEnumType(ErrorNames, {
|
|||||||
export const ErrorDataUnionType = createUnionType({
|
export const ErrorDataUnionType = createUnionType({
|
||||||
name: 'ErrorDataUnion',
|
name: 'ErrorDataUnion',
|
||||||
types: () =>
|
types: () =>
|
||||||
[GraphqlBadRequestDataType, QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, InvalidOauthCallbackCodeDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocActionDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType, UnsupportedClientVersionDataType] as const,
|
[GraphqlBadRequestDataType, QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, InvalidOauthCallbackCodeDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocActionDeniedDataType, DocUpdateBlockedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType, UnsupportedClientVersionDataType] as const,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Socket } from 'socket.io';
|
|||||||
import {
|
import {
|
||||||
CallMetric,
|
CallMetric,
|
||||||
DocNotFound,
|
DocNotFound,
|
||||||
|
DocUpdateBlocked,
|
||||||
GatewayErrorWrapper,
|
GatewayErrorWrapper,
|
||||||
metrics,
|
metrics,
|
||||||
NotInSpace,
|
NotInSpace,
|
||||||
@@ -20,6 +21,7 @@ import {
|
|||||||
SpaceAccessDenied,
|
SpaceAccessDenied,
|
||||||
VersionRejected,
|
VersionRejected,
|
||||||
} from '../../base';
|
} from '../../base';
|
||||||
|
import { Models } from '../../models';
|
||||||
import { CurrentUser } from '../auth';
|
import { CurrentUser } from '../auth';
|
||||||
import {
|
import {
|
||||||
DocReader,
|
DocReader,
|
||||||
@@ -147,7 +149,8 @@ export class SpaceSyncGateway
|
|||||||
private readonly ac: AccessController,
|
private readonly ac: AccessController,
|
||||||
private readonly workspace: PgWorkspaceDocStorageAdapter,
|
private readonly workspace: PgWorkspaceDocStorageAdapter,
|
||||||
private readonly userspace: PgUserspaceDocStorageAdapter,
|
private readonly userspace: PgUserspaceDocStorageAdapter,
|
||||||
private readonly docReader: DocReader
|
private readonly docReader: DocReader,
|
||||||
|
private readonly models: Models
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
handleConnection() {
|
handleConnection() {
|
||||||
@@ -171,7 +174,8 @@ export class SpaceSyncGateway
|
|||||||
client,
|
client,
|
||||||
this.workspace,
|
this.workspace,
|
||||||
this.ac,
|
this.ac,
|
||||||
this.docReader
|
this.docReader,
|
||||||
|
this.models
|
||||||
);
|
);
|
||||||
const userspace = new UserspaceSyncAdapter(client, this.userspace);
|
const userspace = new UserspaceSyncAdapter(client, this.userspace);
|
||||||
|
|
||||||
@@ -501,10 +505,15 @@ abstract class SyncSocketAdapter {
|
|||||||
action: WorkspaceAction
|
action: WorkspaceAction
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
|
||||||
push(spaceId: string, docId: string, updates: Buffer[], editorId: string) {
|
async push(
|
||||||
|
spaceId: string,
|
||||||
|
docId: string,
|
||||||
|
updates: Buffer[],
|
||||||
|
editorId: string
|
||||||
|
) {
|
||||||
// TODO(@forehalo): enable this after 0.19 goes out of life
|
// TODO(@forehalo): enable this after 0.19 goes out of life
|
||||||
// this.assertIn(spaceId);
|
// this.assertIn(spaceId);
|
||||||
return this.storage.pushDocUpdates(spaceId, docId, updates, editorId);
|
return await this.storage.pushDocUpdates(spaceId, docId, updates, editorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
diff(spaceId: string, docId: string, stateVector?: Uint8Array) {
|
diff(spaceId: string, docId: string, stateVector?: Uint8Array) {
|
||||||
@@ -528,18 +537,27 @@ class WorkspaceSyncAdapter extends SyncSocketAdapter {
|
|||||||
client: Socket,
|
client: Socket,
|
||||||
storage: DocStorageAdapter,
|
storage: DocStorageAdapter,
|
||||||
private readonly ac: AccessController,
|
private readonly ac: AccessController,
|
||||||
private readonly docReader: DocReader
|
private readonly docReader: DocReader,
|
||||||
|
private readonly models: Models
|
||||||
) {
|
) {
|
||||||
super(SpaceType.Workspace, client, storage);
|
super(SpaceType.Workspace, client, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
override push(
|
override async push(
|
||||||
spaceId: string,
|
spaceId: string,
|
||||||
docId: string,
|
docId: string,
|
||||||
updates: Buffer[],
|
updates: Buffer[],
|
||||||
editorId: string
|
editorId: string
|
||||||
) {
|
) {
|
||||||
return super.push(spaceId, docId, updates, editorId);
|
const docMeta = await this.models.doc.getMeta(spaceId, docId, {
|
||||||
|
select: {
|
||||||
|
blocked: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (docMeta?.blocked) {
|
||||||
|
throw new DocUpdateBlocked({ spaceId, docId });
|
||||||
|
}
|
||||||
|
return await super.push(spaceId, docId, updates, editorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
override async diff(
|
override async diff(
|
||||||
|
|||||||
@@ -319,12 +319,17 @@ type DocType {
|
|||||||
workspaceId: String!
|
workspaceId: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DocUpdateBlockedDataType {
|
||||||
|
docId: String!
|
||||||
|
spaceId: String!
|
||||||
|
}
|
||||||
|
|
||||||
type EditorType {
|
type EditorType {
|
||||||
avatarUrl: String
|
avatarUrl: String
|
||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidOauthCallbackCodeDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedClientVersionDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | DocUpdateBlockedDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidOauthCallbackCodeDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedClientVersionDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
||||||
|
|
||||||
enum ErrorNames {
|
enum ErrorNames {
|
||||||
ACCESS_DENIED
|
ACCESS_DENIED
|
||||||
@@ -362,6 +367,7 @@ enum ErrorNames {
|
|||||||
DOC_HISTORY_NOT_FOUND
|
DOC_HISTORY_NOT_FOUND
|
||||||
DOC_IS_NOT_PUBLIC
|
DOC_IS_NOT_PUBLIC
|
||||||
DOC_NOT_FOUND
|
DOC_NOT_FOUND
|
||||||
|
DOC_UPDATE_BLOCKED
|
||||||
EARLY_ACCESS_REQUIRED
|
EARLY_ACCESS_REQUIRED
|
||||||
EMAIL_ALREADY_USED
|
EMAIL_ALREADY_USED
|
||||||
EMAIL_TOKEN_NOT_FOUND
|
EMAIL_TOKEN_NOT_FOUND
|
||||||
|
|||||||
Reference in New Issue
Block a user