refactor: rename all page query to doc (#10019)

This commit is contained in:
forehalo
2025-02-07 11:40:59 +00:00
parent 4e00ddd5f1
commit 0b9d30b55a
34 changed files with 658 additions and 643 deletions
+20 -20
View File
@@ -25,16 +25,16 @@ model User {
features UserFeature[]
userStripeCustomer UserStripeCustomer?
workspacePermissions WorkspaceUserPermission[]
pagePermissions WorkspacePageUserPermission[]
docPermissions WorkspaceDocUserPermission[]
connectedAccounts ConnectedAccount[]
sessions UserSession[]
aiSessions AiSession[]
updatedRuntimeConfigs RuntimeConfig[]
userSnapshots UserSnapshot[]
createdSnapshot Snapshot[] @relation("createdSnapshot")
updatedSnapshot Snapshot[] @relation("updatedSnapshot")
createdUpdate Update[] @relation("createdUpdate")
createdHistory SnapshotHistory[] @relation("createdHistory")
createdSnapshot Snapshot[] @relation("createdSnapshot")
updatedSnapshot Snapshot[] @relation("updatedSnapshot")
createdUpdate Update[] @relation("createdUpdate")
createdHistory SnapshotHistory[] @relation("createdHistory")
@@index([email])
@@map("users")
@@ -102,11 +102,11 @@ model Workspace {
enableAi Boolean @default(true) @map("enable_ai")
enableUrlPreview Boolean @default(false) @map("enable_url_preview")
features WorkspaceFeature[]
pages WorkspacePage[]
permissions WorkspaceUserPermission[]
pagePermissions WorkspacePageUserPermission[]
blobs Blob[]
features WorkspaceFeature[]
docs WorkspaceDoc[]
permissions WorkspaceUserPermission[]
docPermissions WorkspaceDocUserPermission[]
blobs Blob[]
@@map("workspaces")
}
@@ -116,18 +116,18 @@ model Workspace {
// We won't make sure every page has a corresponding record in this table.
// Only the ones that have ever changed will have records here,
// and for others we will make sure it's has a default value return in our business logic.
model WorkspacePage {
workspaceId String @map("workspace_id") @db.VarChar
pageId String @map("page_id") @db.VarChar
public Boolean @default(false)
model WorkspaceDoc {
workspaceId String @map("workspace_id") @db.VarChar
docId String @map("page_id") @db.VarChar
public Boolean @default(false)
// Workspace user's default role in this page, default is `Manager`
defaultRole Int @default(30) @db.SmallInt
defaultRole Int @default(30) @db.SmallInt
// Page/Edgeless
mode Int @default(0) @db.SmallInt
mode Int @default(0) @db.SmallInt
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
@@id([workspaceId, pageId])
@@id([workspaceId, docId])
@@map("workspace_pages")
}
@@ -162,9 +162,9 @@ model WorkspaceUserPermission {
@@map("workspace_user_permissions")
}
model WorkspacePageUserPermission {
model WorkspaceDocUserPermission {
workspaceId String @map("workspace_id") @db.VarChar
pageId String @map("page_id") @db.VarChar
docId String @map("page_id") @db.VarChar
userId String @map("user_id") @db.VarChar
// External/Reader/Editor/Manager/Owner
type Int @db.SmallInt
@@ -173,7 +173,7 @@ model WorkspacePageUserPermission {
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
@@id([workspaceId, pageId, userId])
@@id([workspaceId, docId, userId])
@@map("workspace_page_user_permissions")
}
@@ -49,7 +49,7 @@ test.after(async t => {
test('should create page with default mode and public false', async t => {
const page = await t.context.page.upsert(workspace.id, 'page1');
t.is(page.workspaceId, workspace.id);
t.is(page.pageId, 'page1');
t.is(page.docId, 'page1');
t.is(page.mode, PublicPageMode.Page);
t.is(page.public, false);
});
@@ -110,7 +110,7 @@ test('should get public pages of a workspace', async t => {
await t.context.page.upsert(workspace.id, 'page3');
const pages = await t.context.page.findPublics(workspace.id);
t.is(pages.length, 2);
t.deepEqual(pages.map(p => p.pageId).sort(), ['page1', 'page2']);
t.deepEqual(pages.map(p => p.docId).sort(), ['page1', 'page2']);
});
test('should grant a member to access a page', async t => {
@@ -23,6 +23,7 @@ import {
createInviteLink,
createTestingApp,
createWorkspace,
docGrantedUsersList,
getInviteInfo,
getInviteLink,
getWorkspace,
@@ -31,7 +32,6 @@ import {
inviteUser,
inviteUsers,
leaveWorkspace,
pageGrantedUsersList,
revokeDocUserRoles,
revokeInviteLink,
revokeMember,
@@ -39,7 +39,7 @@ import {
signUp,
sleep,
TestingApp,
updatePageDefaultRole,
updateDocDefaultRole,
UserAuthedType,
} from './utils';
@@ -817,13 +817,13 @@ test('should be able to grant and revoke users role in page', async t => {
read,
external,
} = await init(app, 5);
const pageId = nanoid();
const docId = nanoid();
const res = await grantDocUserRoles(
app,
admin.token.token,
ws.id,
pageId,
docId,
[read.id, write.id],
DocRole.Manager
);
@@ -840,7 +840,7 @@ test('should be able to grant and revoke users role in page', async t => {
app,
admin.token.token,
ws.id,
pageId,
docId,
[read.id],
DocRole.Reader
);
@@ -849,7 +849,7 @@ test('should be able to grant and revoke users role in page', async t => {
app,
read.token.token,
ws.id,
pageId,
docId,
[external.id],
DocRole.Editor
);
@@ -859,15 +859,15 @@ test('should be able to grant and revoke users role in page', async t => {
},
});
const pageUsersList = await pageGrantedUsersList(
const docUsersList = await docGrantedUsersList(
app,
admin.token.token,
ws.id,
pageId
docId
);
t.is(pageUsersList.data.workspace.pageGrantedUsersList.totalCount, 3);
t.is(docUsersList.data.workspace.doc.grantedUsersList.totalCount, 3);
const externalRole =
pageUsersList.data.workspace.pageGrantedUsersList.edges.find(
docUsersList.data.workspace.doc.grantedUsersList.edges.find(
(edge: any) => edge.node.user.id === external.id
)?.node.role;
t.is(externalRole, DocRole[DocRole.Editor]);
@@ -877,18 +877,18 @@ test('should be able to grant and revoke users role in page', async t => {
test('should be able to change the default role in page', async t => {
const { app } = t.context;
const { teamWorkspace: ws, admin } = await init(app, 5);
const pageId = nanoid();
const res = await updatePageDefaultRole(
const docId = nanoid();
const res = await updateDocDefaultRole(
app,
admin.token.token,
ws.id,
pageId,
docId,
DocRole.Reader
);
t.deepEqual(res.body, {
data: {
updatePageDefaultRole: true,
updateDocDefaultRole: true,
},
});
});
@@ -902,53 +902,53 @@ test('default page role should be able to override the workspace role', async t
external,
} = await init(app, 5);
const pageId = nanoid();
const docId = nanoid();
const res = await updatePageDefaultRole(
const res = await updateDocDefaultRole(
app,
admin.token.token,
workspace.id,
pageId,
docId,
DocRole.Manager
);
t.deepEqual(res.body, {
data: {
updatePageDefaultRole: true,
updateDocDefaultRole: true,
},
});
// reader can manage the page if the page default role is Manager
{
const readerRes = await updatePageDefaultRole(
const readerRes = await updateDocDefaultRole(
app,
read.token.token,
workspace.id,
pageId,
docId,
DocRole.Manager
);
t.deepEqual(readerRes.body, {
data: {
updatePageDefaultRole: true,
updateDocDefaultRole: true,
},
});
}
// external can't manage the page even if the page default role is Manager
{
const externalRes = await updatePageDefaultRole(
const externalRes = await updateDocDefaultRole(
app,
external.token.token,
workspace.id,
pageId,
docId,
DocRole.Manager
);
t.like(externalRes.body, {
errors: [
{
message: `You do not have permission to access doc ${pageId} under Space ${workspace.id}.`,
message: `You do not have permission to access doc ${docId} under Space ${workspace.id}.`,
},
],
});
@@ -958,13 +958,13 @@ test('default page role should be able to override the workspace role', async t
test('should be able to grant and revoke doc user role', async t => {
const { app } = t.context;
const { teamWorkspace: ws, admin, read, external } = await init(app, 5);
const pageId = nanoid();
const docId = nanoid();
const res = await grantDocUserRoles(
app,
admin.token.token,
ws.id,
pageId,
docId,
[external.id],
DocRole.Manager
);
@@ -981,7 +981,7 @@ test('should be able to grant and revoke doc user role', async t => {
app,
external.token.token,
ws.id,
pageId,
docId,
[read.id],
DocRole.Manager
);
@@ -999,7 +999,7 @@ test('should be able to grant and revoke doc user role', async t => {
app,
admin.token.token,
ws.id,
pageId,
docId,
external.id
);
@@ -1014,14 +1014,14 @@ test('should be able to grant and revoke doc user role', async t => {
app,
external.token.token,
ws.id,
pageId,
docId,
read.id
);
t.like(externalRes.body, {
errors: [
{
message: `You do not have permission to access doc ${pageId} under Space ${ws.id}.`,
message: `You do not have permission to access doc ${docId} under Space ${ws.id}.`,
},
],
});
@@ -1031,7 +1031,7 @@ test('should be able to grant and revoke doc user role', async t => {
test('update page default role should throw error if the space does not exist', async t => {
const { app } = t.context;
const { admin } = await init(app, 5);
const pageId = nanoid();
const docId = nanoid();
const nonExistWorkspaceId = 'non-exist-workspace';
const res = await request(app.getHttpServer())
.post('/graphql')
@@ -1040,9 +1040,9 @@ test('update page default role should throw error if the space does not exist',
.send({
query: `
mutation {
updatePageDefaultRole(input: {
updateDocDefaultRole(input: {
workspaceId: "${nonExistWorkspaceId}",
docId: "${pageId}",
docId: "${docId}",
role: Manager,
})
}
@@ -1052,7 +1052,7 @@ test('update page default role should throw error if the space does not exist',
t.like(res.body, {
errors: [
{
message: `You do not have permission to access doc ${pageId} under Space ${nonExistWorkspaceId}.`,
message: `You do not have permission to access doc ${docId} under Space ${nonExistWorkspaceId}.`,
},
],
});
@@ -54,7 +54,7 @@ export function revokeDocUserRoles(
});
}
export function updatePageDefaultRole(
export function updateDocDefaultRole(
app: INestApplication,
token: string,
workspaceId: string,
@@ -68,7 +68,7 @@ export function updatePageDefaultRole(
.send({
query: `
mutation {
updatePageDefaultRole(input: {
updateDocDefaultRole(input: {
workspaceId: "${workspaceId}",
docId: "${docId}",
role: ${DocRole[role]}
@@ -78,7 +78,7 @@ export function updatePageDefaultRole(
});
}
export async function pageGrantedUsersList(
export async function docGrantedUsersList(
app: INestApplication,
token: string,
workspaceId: string,
@@ -94,14 +94,16 @@ export async function pageGrantedUsersList(
query: `
query {
workspace(id: "${workspaceId}") {
pageGrantedUsersList(pageId: "${docId}", pagination: { first: ${first}, offset: ${offset} }) {
totalCount
edges {
doc(docId: "${docId}") {
grantedUsersList(pagination: { first: ${first}, offset: ${offset} }) {
totalCount
edges {
cursor
node {
role
user {
id
node {
role
user {
id
}
}
}
}
@@ -103,11 +103,11 @@ export async function updateWorkspace(
return res.body.data.updateWorkspace.public;
}
export async function publishPage(
export async function publishDoc(
app: INestApplication,
token: string,
workspaceId: string,
pageId: string
docId: string
) {
const res = await request(app.getHttpServer())
.post(gql)
@@ -116,7 +116,7 @@ export async function publishPage(
.send({
query: `
mutation {
publishPage(workspaceId: "${workspaceId}", pageId: "${pageId}") {
publishDoc(workspaceId: "${workspaceId}", docId: "${docId}") {
id
mode
}
@@ -124,14 +124,14 @@ export async function publishPage(
`,
})
.expect(200);
return res.body.errors?.[0]?.message || res.body.data?.publishPage;
return res.body.errors?.[0]?.message || res.body.data?.publishDoc;
}
export async function revokePublicPage(
export async function revokePublicDoc(
app: INestApplication,
token: string,
workspaceId: string,
pageId: string
docId: string
) {
const res = await request(app.getHttpServer())
.post(gql)
@@ -140,7 +140,7 @@ export async function revokePublicPage(
.send({
query: `
mutation {
revokePublicPage(workspaceId: "${workspaceId}", pageId: "${pageId}") {
revokePublicDoc(workspaceId: "${workspaceId}", docId: "${docId}") {
id
mode
public
@@ -149,7 +149,7 @@ export async function revokePublicPage(
`,
})
.expect(200);
return res.body.errors?.[0]?.message || res.body.data?.revokePublicPage;
return res.body.errors?.[0]?.message || res.body.data?.revokePublicDoc;
}
export async function grantMember(
@@ -10,8 +10,8 @@ import {
createWorkspace,
getWorkspacePublicPages,
inviteUser,
publishPage,
revokePublicPage,
publishDoc,
revokePublicDoc,
signUp,
TestingApp,
updateWorkspace,
@@ -83,8 +83,8 @@ test('should share a page', async t => {
const workspace = await createWorkspace(app, u1.token.token);
const share = await publishPage(app, u1.token.token, workspace.id, 'page1');
t.is(share.id, 'page1', 'failed to share page');
const share = await publishDoc(app, u1.token.token, workspace.id, 'doc1');
t.is(share.id, 'doc1', 'failed to share doc');
const pages = await getWorkspacePublicPages(
app,
u1.token.token,
@@ -93,8 +93,8 @@ test('should share a page', async t => {
t.is(pages.length, 1, 'failed to get shared pages');
t.deepEqual(
pages[0],
{ id: 'page1', mode: 'Page' },
'failed to get shared page: page1'
{ id: 'doc1', mode: 'Page' },
'failed to get shared doc: doc1'
);
const resp1 = await request(app.getHttpServer())
@@ -107,53 +107,48 @@ test('should share a page', async t => {
t.is(resp2.statusCode, 200, 'failed to get root doc with public pages');
const resp3 = await request(app.getHttpServer())
.get(`/api/workspaces/${workspace.id}/docs/page1`)
.get(`/api/workspaces/${workspace.id}/docs/doc1`)
.auth(u1.token.token, { type: 'bearer' });
// 404 because we don't put the page doc to server
t.is(resp3.statusCode, 404, 'failed to get shared doc with u1 token');
const resp4 = await request(app.getHttpServer()).get(
`/api/workspaces/${workspace.id}/docs/page1`
`/api/workspaces/${workspace.id}/docs/doc1`
);
// 404 because we don't put the page doc to server
t.is(resp4.statusCode, 404, 'should not get shared doc without token');
const msg1 = await publishPage(app, u2.token.token, 'not_exists_ws', 'page2');
const msg1 = await publishDoc(app, u2.token.token, 'not_exists_ws', 'doc2');
t.is(
msg1,
'You do not have permission to access doc page2 under Space not_exists_ws.',
'unauthorized user can share page'
'You do not have permission to access doc doc2 under Space not_exists_ws.',
'unauthorized user can share doc'
);
const msg2 = await revokePublicPage(
const msg2 = await revokePublicDoc(
app,
u2.token.token,
'not_exists_ws',
'page2'
'doc2'
);
t.is(
msg2,
'You do not have permission to access doc page2 under Space not_exists_ws.',
'unauthorized user can share page'
'You do not have permission to access doc doc2 under Space not_exists_ws.',
'unauthorized user can share doc'
);
const revoke = await revokePublicPage(
const revoke = await revokePublicDoc(
app,
u1.token.token,
workspace.id,
'page1'
'doc1'
);
t.false(revoke.public, 'failed to revoke page');
t.false(revoke.public, 'failed to revoke doc');
const pages2 = await getWorkspacePublicPages(
app,
u1.token.token,
workspace.id
);
t.is(pages2.length, 0, 'failed to get shared pages');
const msg4 = await revokePublicPage(
app,
u1.token.token,
workspace.id,
'page3'
);
t.is(msg4, 'Page is not public');
const msg4 = await revokePublicDoc(app, u1.token.token, workspace.id, 'doc3');
t.is(msg4, 'Doc is not public');
const pages3 = await getWorkspacePublicPages(
app,
@@ -41,7 +41,7 @@ test.before(async t => {
t.context.storage = app.get(WorkspaceBlobStorage);
t.context.workspace = app.get(PgWorkspaceDocStorageAdapter);
await db.workspacePage.create({
await db.workspaceDoc.create({
data: {
workspace: {
create: {
@@ -49,12 +49,12 @@ test.before(async t => {
public: true,
},
},
pageId: 'private',
docId: 'private',
public: false,
},
});
await db.workspacePage.create({
await db.workspaceDoc.create({
data: {
workspace: {
create: {
@@ -62,12 +62,12 @@ test.before(async t => {
public: false,
},
},
pageId: 'public',
docId: 'public',
public: true,
},
});
await db.workspacePage.create({
await db.workspaceDoc.create({
data: {
workspace: {
create: {
@@ -75,7 +75,7 @@ test.before(async t => {
public: false,
},
},
pageId: 'private',
docId: 'private',
public: false,
},
});
@@ -261,8 +261,8 @@ test('should be able to change page publish mode', async t => {
t.is(res.status, HttpStatus.OK);
t.is(res.get('publish-mode'), 'page');
await db.workspacePage.update({
where: { workspaceId_pageId: { workspaceId: 'private', pageId: 'public' } },
await db.workspaceDoc.update({
where: { workspaceId_docId: { workspaceId: 'private', docId: 'public' } },
data: { mode: 1 },
});
+10 -10
View File
@@ -428,13 +428,13 @@ export const USER_FRIENDLY_ERRORS = {
message: ({ spaceId, blobId }) =>
`Blob ${blobId} not found in Space ${spaceId}.`,
},
expect_to_publish_page: {
expect_to_publish_doc: {
type: 'invalid_input',
message: 'Expected to publish a page, not a Space.',
message: 'Expected to publish a doc, not a Space.',
},
expect_to_revoke_public_page: {
expect_to_revoke_public_doc: {
type: 'invalid_input',
message: 'Expected to revoke a public page, not a Space.',
message: 'Expected to revoke a public doc, not a Space.',
},
expect_to_grant_doc_user_roles: {
type: 'invalid_input',
@@ -454,9 +454,9 @@ export const USER_FRIENDLY_ERRORS = {
message: ({ spaceId, docId }) =>
`Expect update roles on doc ${docId} under Space ${spaceId}, not a Space.`,
},
page_is_not_public: {
doc_is_not_public: {
type: 'bad_request',
message: 'Page is not public.',
message: 'Doc is not public.',
},
failed_to_save_updates: {
type: 'internal_server_error',
@@ -470,13 +470,13 @@ export const USER_FRIENDLY_ERRORS = {
type: 'action_forbidden',
message: 'A Team workspace is required to perform this action.',
},
page_default_role_can_not_be_owner: {
doc_default_role_can_not_be_owner: {
type: 'invalid_input',
message: 'Page default role can not be owner.',
message: 'Doc default role can not be owner.',
},
can_not_batch_grant_page_owner_permissions: {
can_not_batch_grant_doc_owner_permissions: {
type: 'invalid_input',
message: 'Can not batch grant page owner permissions.',
message: 'Can not batch grant doc owner permissions.',
},
// Subscription Errors
@@ -337,15 +337,15 @@ export class BlobNotFound extends UserFriendlyError {
}
}
export class ExpectToPublishPage extends UserFriendlyError {
export class ExpectToPublishDoc extends UserFriendlyError {
constructor(message?: string) {
super('invalid_input', 'expect_to_publish_page', message);
super('invalid_input', 'expect_to_publish_doc', message);
}
}
export class ExpectToRevokePublicPage extends UserFriendlyError {
export class ExpectToRevokePublicDoc extends UserFriendlyError {
constructor(message?: string) {
super('invalid_input', 'expect_to_revoke_public_page', message);
super('invalid_input', 'expect_to_revoke_public_doc', message);
}
}
@ObjectType()
@@ -382,9 +382,9 @@ export class ExpectToUpdateDocUserRole extends UserFriendlyError {
}
}
export class PageIsNotPublic extends UserFriendlyError {
export class DocIsNotPublic extends UserFriendlyError {
constructor(message?: string) {
super('bad_request', 'page_is_not_public', message);
super('bad_request', 'doc_is_not_public', message);
}
}
@@ -406,15 +406,15 @@ export class ActionForbiddenOnNonTeamWorkspace extends UserFriendlyError {
}
}
export class PageDefaultRoleCanNotBeOwner extends UserFriendlyError {
export class DocDefaultRoleCanNotBeOwner extends UserFriendlyError {
constructor(message?: string) {
super('invalid_input', 'page_default_role_can_not_be_owner', message);
super('invalid_input', 'doc_default_role_can_not_be_owner', message);
}
}
export class CanNotBatchGrantPageOwnerPermissions extends UserFriendlyError {
export class CanNotBatchGrantDocOwnerPermissions extends UserFriendlyError {
constructor(message?: string) {
super('invalid_input', 'can_not_batch_grant_page_owner_permissions', message);
super('invalid_input', 'can_not_batch_grant_doc_owner_permissions', message);
}
}
@ObjectType()
@@ -764,17 +764,17 @@ export enum ErrorNames {
INVALID_HISTORY_TIMESTAMP,
DOC_HISTORY_NOT_FOUND,
BLOB_NOT_FOUND,
EXPECT_TO_PUBLISH_PAGE,
EXPECT_TO_REVOKE_PUBLIC_PAGE,
EXPECT_TO_PUBLISH_DOC,
EXPECT_TO_REVOKE_PUBLIC_DOC,
EXPECT_TO_GRANT_DOC_USER_ROLES,
EXPECT_TO_REVOKE_DOC_USER_ROLES,
EXPECT_TO_UPDATE_DOC_USER_ROLE,
PAGE_IS_NOT_PUBLIC,
DOC_IS_NOT_PUBLIC,
FAILED_TO_SAVE_UPDATES,
FAILED_TO_UPSERT_SNAPSHOT,
ACTION_FORBIDDEN_ON_NON_TEAM_WORKSPACE,
PAGE_DEFAULT_ROLE_CAN_NOT_BE_OWNER,
CAN_NOT_BATCH_GRANT_PAGE_OWNER_PERMISSIONS,
DOC_DEFAULT_ROLE_CAN_NOT_BE_OWNER,
CAN_NOT_BATCH_GRANT_DOC_OWNER_PERMISSIONS,
UNSUPPORTED_SUBSCRIPTION_PLAN,
FAILED_TO_CHECKOUT,
INVALID_CHECKOUT_PARAMETERS,
@@ -16,7 +16,7 @@ export {
fixupDocRole,
mapDocRoleToPermissions,
mapWorkspaceRoleToPermissions,
PublicPageMode,
PublicDocMode,
WORKSPACE_ACTIONS,
type WorkspaceActionPermissions,
WorkspaceRole,
@@ -1,10 +1,10 @@
import { Injectable, Logger } from '@nestjs/common';
import type { Prisma, WorkspacePageUserPermission } from '@prisma/client';
import type { Prisma, WorkspaceDocUserPermission } from '@prisma/client';
import { PrismaClient, WorkspaceMemberStatus } from '@prisma/client';
import { groupBy } from 'lodash-es';
import {
CanNotBatchGrantPageOwnerPermissions,
CanNotBatchGrantDocOwnerPermissions,
DocAccessDenied,
EventBus,
OnEvent,
@@ -17,7 +17,7 @@ import {
docActionRequiredRole,
docActionRequiredWorkspaceRole,
DocRole,
PublicPageMode,
PublicDocMode,
WorkspaceRole,
} from './types';
@@ -38,10 +38,10 @@ export class PermissionService {
return;
}
await this.prisma.workspacePageUserPermission.createMany({
await this.prisma.workspaceDocUserPermission.createMany({
data: {
workspaceId,
pageId: docId,
docId,
userId: editor,
type: DocRole.Owner,
createdAt: new Date(),
@@ -172,7 +172,7 @@ export class PermissionService {
// if workspace is public or have any public page, then allow to access
const [isPublicWorkspace, publicPages] = await Promise.all([
this.tryCheckWorkspace(ws, user, WorkspaceRole.Collaborator),
this.prisma.workspacePage.count({
this.prisma.workspaceDoc.count({
where: {
workspaceId: ws,
public: true,
@@ -555,17 +555,17 @@ export class PermissionService {
async tryCheckPage(
ws: string,
page: string,
doc: string,
action: DocAction,
user?: string
) {
const role = docActionRequiredRole(action);
// check whether page is public
if (action === 'Doc.Read') {
const count = await this.prisma.workspacePage.count({
const count = await this.prisma.workspaceDoc.count({
where: {
workspaceId: ws,
pageId: page,
docId: doc,
public: true,
},
});
@@ -579,20 +579,20 @@ export class PermissionService {
if (user) {
const [roleEntity, pageEntity, workspaceRoleEntity] = await Promise.all([
this.prisma.workspacePageUserPermission.findFirst({
this.prisma.workspaceDocUserPermission.findFirst({
where: {
workspaceId: ws,
pageId: page,
docId: doc,
userId: user,
},
select: {
type: true,
},
}),
this.prisma.workspacePage.findFirst({
this.prisma.workspaceDoc.findFirst({
where: {
workspaceId: ws,
pageId: page,
docId: doc,
},
select: {
defaultRole: true,
@@ -629,7 +629,7 @@ export class PermissionService {
}
this.logger.log("User's role is lower than required", {
workspaceId: ws,
docId: page,
docId: doc,
userId: user,
workspaceRole: workspaceRoleEntity
? WorkspaceRole[workspaceRoleEntity.type]
@@ -651,24 +651,24 @@ export class PermissionService {
);
}
async isPublicPage(ws: string, page: string) {
return this.prisma.workspacePage
async isPublicPage(ws: string, doc: string) {
return this.prisma.workspaceDoc
.count({
where: {
workspaceId: ws,
pageId: page,
docId: doc,
public: true,
},
})
.then(count => count > 0);
}
async publishPage(ws: string, page: string, mode = PublicPageMode.Page) {
return this.prisma.workspacePage.upsert({
async publishPage(ws: string, doc: string, mode = PublicDocMode.Page) {
return this.prisma.workspaceDoc.upsert({
where: {
workspaceId_pageId: {
workspaceId_docId: {
workspaceId: ws,
pageId: page,
docId: doc,
},
},
update: {
@@ -677,19 +677,19 @@ export class PermissionService {
},
create: {
workspaceId: ws,
pageId: page,
docId: doc,
mode,
public: true,
},
});
}
async revokePublicPage(ws: string, page: string) {
return this.prisma.workspacePage.upsert({
async revokePublicPage(ws: string, doc: string) {
return this.prisma.workspaceDoc.upsert({
where: {
workspaceId_pageId: {
workspaceId_docId: {
workspaceId: ws,
pageId: page,
docId: doc,
},
},
update: {
@@ -697,20 +697,20 @@ export class PermissionService {
},
create: {
workspaceId: ws,
pageId: page,
docId: doc,
public: false,
},
});
}
async grantPage(ws: string, page: string, user: string, permission: DocRole) {
async grantPage(ws: string, doc: string, user: string, permission: DocRole) {
const [p] = await this.prisma.$transaction(
[
this.prisma.workspacePageUserPermission.upsert({
this.prisma.workspaceDocUserPermission.upsert({
where: {
workspaceId_pageId_userId: {
workspaceId_docId_userId: {
workspaceId: ws,
pageId: page,
docId: doc,
userId: user,
},
},
@@ -719,7 +719,7 @@ export class PermissionService {
},
create: {
workspaceId: ws,
pageId: page,
docId: doc,
userId: user,
type: permission,
},
@@ -727,10 +727,10 @@ export class PermissionService {
// If the new permission is owner, we need to revoke old owner
permission === DocRole.Owner
? this.prisma.workspacePageUserPermission.updateMany({
? this.prisma.workspaceDocUserPermission.updateMany({
where: {
workspaceId: ws,
pageId: page,
docId: doc,
type: DocRole.Owner,
userId: {
not: user,
@@ -744,14 +744,14 @@ export class PermissionService {
].filter(Boolean) as Prisma.PrismaPromise<any>[]
);
return p as WorkspacePageUserPermission;
return p as WorkspaceDocUserPermission;
}
async revokePage(ws: string, page: string, user: string) {
const result = await this.prisma.workspacePageUserPermission.deleteMany({
async revokePage(ws: string, doc: string, user: string) {
const result = await this.prisma.workspaceDocUserPermission.deleteMany({
where: {
workspaceId: ws,
pageId: page,
docId: doc,
userId: user,
type: {
// We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading
@@ -765,7 +765,7 @@ export class PermissionService {
async batchGrantPage(
workspaceId: string,
pageId: string,
docId: string,
userIds: string[],
role: DocRole
) {
@@ -774,14 +774,14 @@ export class PermissionService {
}
if (role === DocRole.Owner) {
throw new CanNotBatchGrantPageOwnerPermissions();
throw new CanNotBatchGrantDocOwnerPermissions();
}
const result = await this.prisma.workspacePageUserPermission.createMany({
const result = await this.prisma.workspaceDocUserPermission.createMany({
skipDuplicates: true,
data: userIds.map(id => ({
workspaceId,
pageId,
docId,
userId: id,
type: role,
})),
@@ -1,6 +1,6 @@
import { LeafPaths, LeafVisitor } from '../../base';
export enum PublicPageMode {
export enum PublicDocMode {
Page,
Edgeless,
}
@@ -13,7 +13,7 @@ import {
} from '../../base';
import { CurrentUser, Public } from '../auth';
import { PgWorkspaceDocStorageAdapter } from '../doc';
import { PermissionService, PublicPageMode } from '../permission';
import { PermissionService, PublicDocMode } from '../permission';
import { WorkspaceBlobStorage } from '../storage';
import { DocID } from '../utils/doc';
@@ -109,16 +109,16 @@ export class WorkspacesController {
if (!docId.isWorkspace) {
// fetch the publish page mode for publish page
const publishPage = await this.prisma.workspacePage.findUnique({
const publishPage = await this.prisma.workspaceDoc.findUnique({
where: {
workspaceId_pageId: {
workspaceId_docId: {
workspaceId: docId.workspace,
pageId: docId.guid,
docId: docId.guid,
},
},
});
const publishPageMode =
publishPage?.mode === PublicPageMode.Edgeless ? 'edgeless' : 'page';
publishPage?.mode === PublicDocMode.Edgeless ? 'edgeless' : 'page';
res.setHeader('publish-mode', publishPageMode);
}
@@ -10,9 +10,10 @@ import { UserModule } from '../user';
import { WorkspacesController } from './controller';
import {
DocHistoryResolver,
PagePermissionResolver,
DocResolver,
TeamWorkspaceResolver,
WorkspaceBlobResolver,
WorkspaceDocResolver,
WorkspaceResolver,
WorkspaceService,
} from './resolvers';
@@ -31,7 +32,8 @@ import {
providers: [
WorkspaceResolver,
TeamWorkspaceResolver,
PagePermissionResolver,
WorkspaceDocResolver,
DocResolver,
DocHistoryResolver,
WorkspaceBlobResolver,
WorkspaceService,
@@ -10,18 +10,18 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import type { WorkspacePage as PrismaWorkspacePage } from '@prisma/client';
import type { WorkspaceDoc as PrismaWorkspaceDoc } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import {
DocAccessDenied,
DocDefaultRoleCanNotBeOwner,
DocIsNotPublic,
ExpectToGrantDocUserRoles,
ExpectToPublishPage,
ExpectToPublishDoc,
ExpectToRevokeDocUserRoles,
ExpectToRevokePublicPage,
ExpectToRevokePublicDoc,
ExpectToUpdateDocUserRole,
PageDefaultRoleCanNotBeOwner,
PageIsNotPublic,
paginate,
Paginated,
PaginationInput,
@@ -36,27 +36,27 @@ import {
fixupDocRole,
mapDocRoleToPermissions,
PermissionService,
PublicPageMode,
PublicDocMode,
} from '../../permission';
import { PublicUserType } from '../../user';
import { DocID } from '../../utils/doc';
import { WorkspaceType } from '../types';
registerEnumType(PublicPageMode, {
name: 'PublicPageMode',
description: 'The mode which the public page default in',
registerEnumType(PublicDocMode, {
name: 'PublicDocMode',
description: 'The mode which the public doc default in',
});
@ObjectType()
class WorkspacePage implements Partial<PrismaWorkspacePage> {
class DocType implements Partial<PrismaWorkspaceDoc> {
@Field(() => String, { name: 'id' })
pageId!: string;
docId!: string;
@Field()
workspaceId!: string;
@Field(() => PublicPageMode)
mode!: PublicPageMode;
@Field(() => PublicDocMode)
mode!: PublicDocMode;
@Field()
public!: boolean;
@@ -105,7 +105,7 @@ class RevokeDocUserRoleInput {
}
@InputType()
class UpdatePageDefaultRoleInput {
class UpdateDocDefaultRoleInput {
@Field(() => String)
docId!: string;
@@ -144,13 +144,7 @@ const DocPermissions = registerObjectType<DocActionPermissions>(
);
@ObjectType()
class DocType {
@Field(() => String)
id!: string;
@Field(() => Boolean)
public!: boolean;
export class DocRolePermissions {
@Field(() => DocRole)
role!: DocRole;
@@ -159,8 +153,207 @@ class DocType {
}
@Resolver(() => WorkspaceType)
export class PagePermissionResolver {
private readonly logger = new Logger(PagePermissionResolver.name);
export class WorkspaceDocResolver {
private readonly logger = new Logger(WorkspaceDocResolver.name);
constructor(
private readonly prisma: PrismaClient,
private readonly permission: PermissionService
) {}
@ResolveField(() => [DocType], {
complexity: 2,
deprecationReason: 'use [WorkspaceType.publicDocs] instead',
})
async publicPages(@Parent() workspace: WorkspaceType) {
return this.publicDocs(workspace);
}
@ResolveField(() => [DocType], {
description: 'Get public docs of a workspace',
complexity: 2,
})
async publicDocs(@Parent() workspace: WorkspaceType) {
return this.prisma.workspaceDoc.findMany({
where: {
workspaceId: workspace.id,
public: true,
},
});
}
@ResolveField(() => DocType, {
description: 'Get public page of a workspace by page id.',
complexity: 2,
nullable: true,
deprecationReason: 'use [WorkspaceType.publicDoc] instead',
})
async publicPage(
@Parent() workspace: WorkspaceType,
@Args('pageId') pageId: string
) {
return this.publicDoc(workspace, pageId);
}
@ResolveField(() => DocType, {
description: 'Get public page of a workspace by page id.',
complexity: 2,
nullable: true,
})
async publicDoc(
@Parent() workspace: WorkspaceType,
@Args('docId') docId: string
) {
return this.prisma.workspaceDoc.findFirst({
where: {
workspaceId: workspace.id,
docId,
public: true,
},
});
}
@ResolveField(() => DocType, {
description: 'Get get with given id',
complexity: 2,
})
async doc(
@Parent() workspace: WorkspaceType,
@Args('docId') docId: string
): Promise<DocType> {
const doc = await this.prisma.workspaceDoc.findFirst({
where: {
workspaceId: workspace.id,
docId,
},
});
return (
doc ?? {
docId,
workspaceId: workspace.id,
public: false,
mode: PublicDocMode.Page,
}
);
}
@Mutation(() => DocType, {
deprecationReason: 'use publishDoc instead',
})
async publishPage(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string,
@Args({
name: 'mode',
type: () => PublicDocMode,
nullable: true,
defaultValue: PublicDocMode.Page,
})
mode: PublicDocMode
) {
return this.publishDoc(user, workspaceId, pageId, mode);
}
@Mutation(() => DocType)
async publishDoc(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('docId') rawDocId: string,
@Args({
name: 'mode',
type: () => PublicDocMode,
nullable: true,
defaultValue: PublicDocMode.Page,
})
mode: PublicDocMode
) {
const docId = new DocID(rawDocId, workspaceId);
if (docId.isWorkspace) {
this.logger.error('Expect to publish doc, but it is a workspace', {
workspaceId,
docId: rawDocId,
});
throw new ExpectToPublishDoc();
}
await this.permission.checkPagePermission(
docId.workspace,
docId.guid,
'Doc.Publish',
user.id
);
this.logger.log('Publish page', {
workspaceId,
docId: rawDocId,
mode,
});
return this.permission.publishPage(docId.workspace, docId.guid, mode);
}
@Mutation(() => DocType, {
deprecationReason: 'use revokePublicDoc instead',
})
async revokePublicPage(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('docId') docId: string
) {
return this.revokePublicDoc(user, workspaceId, docId);
}
@Mutation(() => DocType)
async revokePublicDoc(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('docId') rawDocId: string
) {
const docId = new DocID(rawDocId, workspaceId);
if (docId.isWorkspace) {
this.logger.error('Expect to revoke public doc, but it is a workspace', {
workspaceId,
docId: rawDocId,
});
throw new ExpectToRevokePublicDoc('Expect doc not to be workspace');
}
await this.permission.checkPagePermission(
docId.workspace,
docId.guid,
'Doc.Publish',
user.id
);
const isPublic = await this.permission.isPublicPage(
docId.workspace,
docId.guid
);
if (!isPublic) {
this.logger.log('Expect to revoke public doc, but it is not public', {
workspaceId,
docId: rawDocId,
});
throw new DocIsNotPublic('Doc is not public');
}
this.logger.log('Revoke public doc', {
workspaceId,
docId: rawDocId,
});
return this.permission.revokePublicPage(docId.workspace, docId.guid);
}
}
@Resolver(() => DocType)
export class DocResolver {
private readonly logger = new Logger(DocResolver.name);
constructor(
private readonly prisma: PrismaClient,
@@ -168,96 +361,31 @@ export class PagePermissionResolver {
private readonly models: Models
) {}
/**
* @deprecated
*/
@ResolveField(() => [String], {
description: 'Shared pages of workspace',
complexity: 2,
deprecationReason: 'use WorkspaceType.publicPages',
})
async sharedPages(@Parent() workspace: WorkspaceType) {
const data = await this.prisma.workspacePage.findMany({
where: {
workspaceId: workspace.id,
public: true,
},
});
return data.map(row => row.pageId);
}
@ResolveField(() => [WorkspacePage], {
description: 'Public pages of a workspace',
complexity: 2,
})
async publicPages(@Parent() workspace: WorkspaceType) {
return this.prisma.workspacePage.findMany({
where: {
workspaceId: workspace.id,
public: true,
},
});
}
@ResolveField(() => WorkspacePage, {
description: 'Get public page of a workspace by page id.',
complexity: 2,
nullable: true,
})
async publicPage(
@Parent() workspace: WorkspaceType,
@Args('pageId') pageId: string
) {
return this.prisma.workspacePage.findFirst({
where: {
workspaceId: workspace.id,
pageId,
public: true,
},
});
}
@ResolveField(() => DocType, {
description: 'Check if current user has permission to access the page',
complexity: 2,
})
async pagePermission(
@Parent() workspace: WorkspaceType,
@Args('pageId') pageId: string,
@CurrentUser() user: CurrentUser
): Promise<DocType> {
const page = await this.prisma.workspacePage.findFirst({
where: {
workspaceId: workspace.id,
pageId,
},
select: {
public: true,
},
});
@ResolveField(() => DocPermissions)
async permissions(
@CurrentUser() user: CurrentUser,
@Parent() doc: DocType
): Promise<DocRolePermissions> {
const [permission, workspacePermission] = await this.prisma.$transaction(
tx =>
Promise.all([
tx.workspacePageUserPermission.findFirst({
tx.workspaceDocUserPermission.findFirst({
where: {
workspaceId: workspace.id,
pageId,
workspaceId: doc.workspaceId,
docId: doc.docId,
userId: user.id,
},
}),
tx.workspaceUserPermission.findFirst({
where: {
workspaceId: workspace.id,
workspaceId: doc.workspaceId,
userId: user.id,
},
}),
])
);
return {
id: pageId,
public: page?.public ?? false,
role: permission?.type ?? DocRole.External,
permissions: mapDocRoleToPermissions(
fixupDocRole(workspacePermission?.type, permission?.type)
@@ -266,29 +394,27 @@ export class PagePermissionResolver {
}
@ResolveField(() => PaginatedGrantedDocUserType, {
description: 'Page granted users list',
description: 'paginated doc granted users list',
complexity: 4,
})
async pageGrantedUsersList(
async grantedUsersList(
@CurrentUser() user: CurrentUser,
@Parent() workspace: WorkspaceType,
@Args('pageId') pageId: string,
@Parent() doc: DocType,
@Args('pagination') pagination: PaginationInput
): Promise<PaginatedGrantedDocUserType> {
await this.permission.checkPagePermission(
workspace.id,
pageId,
doc.workspaceId,
doc.docId,
'Doc.Users.Read',
user.id
);
const docId = new DocID(pageId, workspace.id);
const [permissions, totalCount] = await this.prisma.$transaction(tx => {
return Promise.all([
tx.workspacePageUserPermission.findMany({
tx.workspaceDocUserPermission.findMany({
where: {
workspaceId: workspace.id,
pageId: docId.guid,
workspaceId: doc.workspaceId,
docId: doc.docId,
createdAt: pagination.after
? {
gt: pagination.after,
@@ -306,10 +432,10 @@ export class PagePermissionResolver {
take: pagination.first,
skip: pagination.offset,
}),
tx.workspacePageUserPermission.count({
tx.workspaceDocUserPermission.count({
where: {
workspaceId: workspace.id,
pageId: docId.guid,
workspaceId: doc.workspaceId,
docId: doc.docId,
},
}),
]);
@@ -338,121 +464,6 @@ export class PagePermissionResolver {
);
}
/**
* @deprecated
*/
@Mutation(() => Boolean, {
name: 'sharePage',
deprecationReason: 'renamed to publishPage',
})
async deprecatedSharePage(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
await this.publishPage(user, workspaceId, pageId, PublicPageMode.Page);
return true;
}
@Mutation(() => WorkspacePage)
async publishPage(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string,
@Args({
name: 'mode',
type: () => PublicPageMode,
nullable: true,
defaultValue: PublicPageMode.Page,
})
mode: PublicPageMode
) {
const docId = new DocID(pageId, workspaceId);
if (docId.isWorkspace) {
this.logger.error('Expect to publish page, but it is a workspace', {
workspaceId,
pageId,
});
throw new ExpectToPublishPage();
}
await this.permission.checkPagePermission(
docId.workspace,
docId.guid,
'Doc.Publish',
user.id
);
this.logger.log('Publish page', {
workspaceId,
pageId,
mode,
});
return this.permission.publishPage(docId.workspace, docId.guid, mode);
}
/**
* @deprecated
*/
@Mutation(() => Boolean, {
name: 'revokePage',
deprecationReason: 'use revokePublicPage',
})
async deprecatedRevokePage(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
await this.revokePublicPage(user, workspaceId, pageId);
return true;
}
@Mutation(() => WorkspacePage)
async revokePublicPage(
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
const docId = new DocID(pageId, workspaceId);
if (docId.isWorkspace) {
this.logger.error('Expect to revoke public page, but it is a workspace', {
workspaceId,
pageId,
});
throw new ExpectToRevokePublicPage('Expect page not to be workspace');
}
await this.permission.checkPagePermission(
docId.workspace,
docId.guid,
'Doc.Publish',
user.id
);
const isPublic = await this.permission.isPublicPage(
docId.workspace,
docId.guid
);
if (!isPublic) {
this.logger.log('Expect to revoke public page, but it is not public', {
workspaceId,
pageId,
});
throw new PageIsNotPublic('Page is not public');
}
this.logger.log('Revoke public page', {
workspaceId,
pageId,
});
return this.permission.revokePublicPage(docId.workspace, docId.guid);
}
@Mutation(() => Boolean)
async grantDocUserRoles(
@CurrentUser() user: CurrentUser,
@@ -580,13 +591,13 @@ export class PagePermissionResolver {
}
@Mutation(() => Boolean)
async updatePageDefaultRole(
async updateDocDefaultRole(
@CurrentUser() user: CurrentUser,
@Args('input') input: UpdatePageDefaultRoleInput
@Args('input') input: UpdateDocDefaultRoleInput
) {
if (input.role === DocRole.Owner) {
this.logger.log('Page default role can not be owner', input);
throw new PageDefaultRoleCanNotBeOwner();
this.logger.log('Doc default role can not be owner', input);
throw new DocDefaultRoleCanNotBeOwner();
}
const doc = new DocID(input.docId, input.workspaceId);
const pairs = {
@@ -622,11 +633,11 @@ export class PagePermissionResolver {
}
throw error;
}
await this.prisma.workspacePage.upsert({
await this.prisma.workspaceDoc.upsert({
where: {
workspaceId_pageId: {
workspaceId_docId: {
workspaceId: doc.workspace,
pageId: doc.guid,
docId: doc.guid,
},
},
update: {
@@ -634,7 +645,7 @@ export class PagePermissionResolver {
},
create: {
workspaceId: doc.workspace,
pageId: doc.guid,
docId: doc.guid,
defaultRole: input.role,
},
});
@@ -1,6 +1,6 @@
export * from './blob';
export * from './doc';
export * from './history';
export * from './page';
export * from './service';
export * from './team';
export * from './workspace';
+31 -31
View File
@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { Transactional } from '@nestjs-cls/transactional';
import {
type WorkspacePage as Page,
type WorkspacePageUserPermission as PageUserPermission,
type WorkspaceDoc as Page,
type WorkspaceDocUserPermission as PageUserPermission,
} from '@prisma/client';
import { WorkspaceRole } from '../core/permission';
@@ -21,12 +21,12 @@ export class PageModel extends BaseModel {
/**
* Create or update the page.
*/
async upsert(workspaceId: string, pageId: string, data?: UpdatePageInput) {
return await this.db.workspacePage.upsert({
async upsert(workspaceId: string, docId: string, data?: UpdatePageInput) {
return await this.db.workspaceDoc.upsert({
where: {
workspaceId_pageId: {
workspaceId_docId: {
workspaceId,
pageId,
docId,
},
},
update: {
@@ -35,7 +35,7 @@ export class PageModel extends BaseModel {
create: {
...data,
workspaceId,
pageId,
docId,
},
});
}
@@ -45,12 +45,12 @@ export class PageModel extends BaseModel {
* @param isPublic: if true, only return the public page. If false, only return the private page.
* If not set, return public or private both.
*/
async get(workspaceId: string, pageId: string, isPublic?: boolean) {
return await this.db.workspacePage.findUnique({
async get(workspaceId: string, docId: string, isPublic?: boolean) {
return await this.db.workspaceDoc.findUnique({
where: {
workspaceId_pageId: {
workspaceId_docId: {
workspaceId,
pageId,
docId,
},
public: isPublic,
},
@@ -61,7 +61,7 @@ export class PageModel extends BaseModel {
* Find the workspace public pages.
*/
async findPublics(workspaceId: string) {
return await this.db.workspacePage.findMany({
return await this.db.workspaceDoc.findMany({
where: {
workspaceId,
public: true,
@@ -73,7 +73,7 @@ export class PageModel extends BaseModel {
* Get the workspace public pages count.
*/
async getPublicsCount(workspaceId: string) {
return await this.db.workspacePage.count({
return await this.db.workspaceDoc.count({
where: {
workspaceId,
public: true,
@@ -91,15 +91,15 @@ export class PageModel extends BaseModel {
@Transactional()
async grantMember(
workspaceId: string,
pageId: string,
docId: string,
userId: string,
permission: WorkspaceRole = WorkspaceRole.Collaborator
): Promise<PageUserPermission> {
let data = await this.db.workspacePageUserPermission.findUnique({
let data = await this.db.workspaceDocUserPermission.findUnique({
where: {
workspaceId_pageId_userId: {
workspaceId_docId_userId: {
workspaceId,
pageId,
docId,
userId,
},
},
@@ -109,11 +109,11 @@ export class PageModel extends BaseModel {
if (!data || data.type !== permission) {
if (data) {
// Update the permission
data = await this.db.workspacePageUserPermission.update({
data = await this.db.workspaceDocUserPermission.update({
where: {
workspaceId_pageId_userId: {
workspaceId_docId_userId: {
workspaceId,
pageId,
docId,
userId,
},
},
@@ -121,10 +121,10 @@ export class PageModel extends BaseModel {
});
} else {
// Create a new permission
data = await this.db.workspacePageUserPermission.create({
data = await this.db.workspaceDocUserPermission.create({
data: {
workspaceId,
pageId,
docId,
userId,
type: permission,
},
@@ -133,17 +133,17 @@ export class PageModel extends BaseModel {
// If the new permission is owner, we need to revoke old owner
if (permission === WorkspaceRole.Owner) {
await this.db.workspacePageUserPermission.updateMany({
await this.db.workspaceDocUserPermission.updateMany({
where: {
workspaceId,
pageId,
docId,
type: WorkspaceRole.Owner,
userId: { not: userId },
},
data: { type: WorkspaceRole.Admin },
});
this.logger.log(
`Change owner of workspace ${workspaceId} page ${pageId} to user ${userId}`
`Change owner of workspace ${workspaceId} doc ${docId} to user ${userId}`
);
}
return data;
@@ -159,14 +159,14 @@ export class PageModel extends BaseModel {
*/
async isMember(
workspaceId: string,
pageId: string,
docId: string,
userId: string,
permission: WorkspaceRole = WorkspaceRole.Collaborator
) {
const count = await this.db.workspacePageUserPermission.count({
const count = await this.db.workspaceDocUserPermission.count({
where: {
workspaceId,
pageId,
docId,
userId,
type: {
gte: permission,
@@ -180,11 +180,11 @@ export class PageModel extends BaseModel {
* Delete a page member
* Except the owner, the owner can't be deleted.
*/
async deleteMember(workspaceId: string, pageId: string, userId: string) {
const { count } = await this.db.workspacePageUserPermission.deleteMany({
async deleteMember(workspaceId: string, docId: string, userId: string) {
const { count } = await this.db.workspaceDocUserPermission.deleteMany({
where: {
workspaceId,
pageId,
docId,
userId,
type: {
// We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading
+32 -38
View File
@@ -230,10 +230,13 @@ enum DocRole {
}
type DocType {
"""paginated doc granted users list"""
grantedUsersList(pagination: PaginationInput!): PaginatedGrantedDocUserType!
id: String!
mode: PublicDocMode!
permissions: DocPermissions!
public: Boolean!
role: DocRole!
workspaceId: String!
}
type EditorType {
@@ -254,7 +257,7 @@ enum ErrorNames {
CANNOT_DELETE_ALL_ADMIN_ACCOUNT
CANNOT_DELETE_OWN_ACCOUNT
CANT_UPDATE_ONETIME_PAYMENT_SUBSCRIPTION
CAN_NOT_BATCH_GRANT_PAGE_OWNER_PERMISSIONS
CAN_NOT_BATCH_GRANT_DOC_OWNER_PERMISSIONS
CAPTCHA_VERIFICATION_FAILED
COPILOT_ACTION_TAKEN
COPILOT_FAILED_TO_CREATE_MESSAGE
@@ -268,16 +271,18 @@ enum ErrorNames {
COPILOT_SESSION_NOT_FOUND
CUSTOMER_PORTAL_CREATE_FAILED
DOC_ACCESS_DENIED
DOC_DEFAULT_ROLE_CAN_NOT_BE_OWNER
DOC_HISTORY_NOT_FOUND
DOC_IS_NOT_PUBLIC
DOC_NOT_FOUND
EARLY_ACCESS_REQUIRED
EMAIL_ALREADY_USED
EMAIL_TOKEN_NOT_FOUND
EMAIL_VERIFICATION_REQUIRED
EXPECT_TO_GRANT_DOC_USER_ROLES
EXPECT_TO_PUBLISH_PAGE
EXPECT_TO_PUBLISH_DOC
EXPECT_TO_REVOKE_DOC_USER_ROLES
EXPECT_TO_REVOKE_PUBLIC_PAGE
EXPECT_TO_REVOKE_PUBLIC_DOC
EXPECT_TO_UPDATE_DOC_USER_ROLE
FAILED_TO_CHECKOUT
FAILED_TO_SAVE_UPDATES
@@ -306,8 +311,6 @@ enum ErrorNames {
NO_COPILOT_PROVIDER_AVAILABLE
OAUTH_ACCOUNT_ALREADY_CONNECTED
OAUTH_STATE_EXPIRED
PAGE_DEFAULT_ROLE_CAN_NOT_BE_OWNER
PAGE_IS_NOT_PUBLIC
PASSWORD_REQUIRED
QUERY_TOO_LONG
RUNTIME_CONFIG_NOT_FOUND
@@ -621,7 +624,8 @@ type Mutation {
invite(email: String!, permission: Permission @deprecated(reason: "never used"), sendInviteMail: Boolean, workspaceId: String!): String!
inviteBatch(emails: [String!]!, sendInviteMail: Boolean, workspaceId: String!): [InviteResult!]!
leaveWorkspace(sendLeaveMail: Boolean, workspaceId: String!, workspaceName: String @deprecated(reason: "no longer used")): Boolean!
publishPage(mode: PublicPageMode = Page, pageId: String!, workspaceId: String!): WorkspacePage!
publishDoc(docId: String!, mode: PublicDocMode = Page, workspaceId: String!): DocType!
publishPage(mode: PublicDocMode = Page, pageId: String!, workspaceId: String!): DocType! @deprecated(reason: "use publishDoc instead")
recoverDoc(guid: String!, timestamp: DateTime!, workspaceId: String!): DateTime!
releaseDeletedBlobs(workspaceId: String!): Boolean!
@@ -632,23 +636,22 @@ type Mutation {
revoke(userId: String!, workspaceId: String!): Boolean!
revokeDocUserRoles(input: RevokeDocUserRoleInput!): Boolean!
revokeInviteLink(workspaceId: String!): Boolean!
revokePage(pageId: String!, workspaceId: String!): Boolean! @deprecated(reason: "use revokePublicPage")
revokePublicPage(pageId: String!, workspaceId: String!): WorkspacePage!
revokePublicDoc(docId: String!, workspaceId: String!): DocType!
revokePublicPage(docId: String!, workspaceId: String!): DocType! @deprecated(reason: "use revokePublicDoc instead")
sendChangeEmail(callbackUrl: String!, email: String): Boolean!
sendChangePasswordEmail(callbackUrl: String!, email: String @deprecated(reason: "fetched from signed in user")): Boolean!
sendSetPasswordEmail(callbackUrl: String!, email: String @deprecated(reason: "fetched from signed in user")): Boolean!
sendVerifyChangeEmail(callbackUrl: String!, email: String!, token: String!): Boolean!
sendVerifyEmail(callbackUrl: String!): Boolean!
setBlob(blob: Upload!, workspaceId: String!): String!
sharePage(pageId: String!, workspaceId: String!): Boolean! @deprecated(reason: "renamed to publishPage")
"""Update a copilot prompt"""
updateCopilotPrompt(messages: [CopilotPromptMessageInput!]!, name: String!): CopilotPromptType!
"""Update a chat session"""
updateCopilotSession(options: UpdateChatSessionInput!): String!
updateDocDefaultRole(input: UpdateDocDefaultRoleInput!): Boolean!
updateDocUserRole(input: UpdateDocUserRoleInput!): Boolean!
updatePageDefaultRole(input: UpdatePageDefaultRoleInput!): Boolean!
updateProfile(input: UpdateUserInput!): UserType!
"""update server runtime configurable setting"""
@@ -719,8 +722,8 @@ enum Permission {
Owner
}
"""The mode which the public page default in"""
enum PublicPageMode {
"""The mode which the public doc default in"""
enum PublicDocMode {
Edgeless
Page
}
@@ -1005,6 +1008,12 @@ input UpdateChatSessionInput {
sessionId: String!
}
input UpdateDocDefaultRoleInput {
docId: String!
role: DocRole!
workspaceId: String!
}
input UpdateDocUserRoleInput {
docId: String!
role: DocRole!
@@ -1012,12 +1021,6 @@ input UpdateDocUserRoleInput {
workspaceId: String!
}
input UpdatePageDefaultRoleInput {
docId: String!
role: DocRole!
workspaceId: String!
}
input UpdateUserInput {
"""User name"""
name: String
@@ -1128,13 +1131,6 @@ type WorkspaceMembersExceedLimitToDowngradeDataType {
limit: Int!
}
type WorkspacePage {
id: String!
mode: PublicPageMode!
public: Boolean!
workspaceId: String!
}
type WorkspacePageMeta {
createdAt: DateTime!
createdBy: EditorType
@@ -1199,6 +1195,9 @@ type WorkspaceType {
"""Workspace created date"""
createdAt: DateTime!
"""Get get with given id"""
doc(docId: String!): DocType!
"""Enable AI"""
enableAi: Boolean!
@@ -1229,23 +1228,21 @@ type WorkspaceType {
"""Owner of workspace"""
owner: UserType!
"""Page granted users list"""
pageGrantedUsersList(pageId: String!, pagination: PaginationInput!): PaginatedGrantedDocUserType!
"""Cloud page metadata of workspace"""
pageMeta(pageId: String!): WorkspacePageMeta!
"""Check if current user has permission to access the page"""
pagePermission(pageId: String!): DocType!
"""is Public workspace"""
public: Boolean!
"""Get public page of a workspace by page id."""
publicPage(pageId: String!): WorkspacePage
publicDoc(docId: String!): DocType
"""Public pages of a workspace"""
publicPages: [WorkspacePage!]!
"""Get public docs of a workspace"""
publicDocs: [DocType!]!
"""Get public page of a workspace by page id."""
publicPage(pageId: String!): DocType @deprecated(reason: "use [WorkspaceType.publicDoc] instead")
publicPages: [DocType!]! @deprecated(reason: "use [WorkspaceType.publicDocs] instead")
"""quota of workspace"""
quota: WorkspaceQuotaType!
@@ -1253,9 +1250,6 @@ type WorkspaceType {
"""Role of current signed in user in workspace"""
role: Permission!
"""Shared pages of workspace"""
sharedPages: [String!]! @deprecated(reason: "use WorkspaceType.publicPages")
"""The team subscription of the workspace, if exists."""
subscription: SubscriptionType
@@ -6,7 +6,7 @@ import { ServerService } from '@affine/core/modules/cloud';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
import { ShareInfoService } from '@affine/core/modules/share-doc';
import { PublicPageMode } from '@affine/graphql';
import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import {
@@ -92,7 +92,7 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
}
try {
// TODO(@JimmFly): remove mode when we have a better way to handle it
await shareInfoService.shareInfo.enableShare(PublicPageMode.Page);
await shareInfoService.shareInfo.enableShare(PublicDocMode.Page);
track.$.sharePanel.$.createShareLink();
notify.success({
title:
@@ -4,7 +4,7 @@ import { FavoriteTag } from '@affine/core/components/page-list/components/favori
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { PublicPageMode } from '@affine/graphql';
import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import type { DocMeta, Workspace } from '@blocksuite/affine/store';
import { useLiveData, useService } from '@toeverything/infra';
@@ -68,9 +68,9 @@ export const useAllPageListConfig = () => {
allPages: pageMetas,
getPublicMode(id) {
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
if (mode === PublicPageMode.Edgeless) {
if (mode === PublicDocMode.Edgeless) {
return 'edgeless';
} else if (mode === PublicPageMode.Page) {
} else if (mode === PublicDocMode.Page) {
return 'page';
} else {
return undefined;
@@ -3,7 +3,7 @@ import { useBlockSuiteDocMeta } from '@affine/core/components/hooks/use-block-su
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc';
import { WorkspaceService } from '@affine/core/modules/workspace';
import { PublicPageMode } from '@affine/graphql';
import { PublicDocMode } from '@affine/graphql';
import { Trans, useI18n } from '@affine/i18n';
import type { DocMeta } from '@blocksuite/affine/store';
import { FilterIcon } from '@blocksuite/icons/rc';
@@ -76,9 +76,9 @@ export const SelectPage = ({
const getPublicMode = useCallback(
(id: string) => {
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
if (mode === PublicPageMode.Edgeless) {
if (mode === PublicDocMode.Edgeless) {
return 'edgeless';
} else if (mode === PublicPageMode.Page) {
} else if (mode === PublicDocMode.Page) {
return 'page';
} else {
return undefined;
@@ -1,7 +1,7 @@
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { Collection, Filter } from '@affine/env/filter';
import { PublicPageMode } from '@affine/graphql';
import { PublicDocMode } from '@affine/graphql';
import type { DocMeta } from '@blocksuite/affine/store';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useEffect, useMemo } from 'react';
@@ -23,7 +23,7 @@ export const useFilteredPageMetas = (
(id: string) => {
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
return mode
? mode === PublicPageMode.Edgeless
? mode === PublicDocMode.Edgeless
? ('edgeless' as const)
: ('page' as const)
: undefined;
@@ -8,7 +8,7 @@ import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { GlobalContextService } from '@affine/core/modules/global-context';
import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { Collection } from '@affine/env/filter';
import { PublicPageMode } from '@affine/graphql';
import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import type { DocMeta } from '@blocksuite/affine/store';
@@ -161,9 +161,9 @@ const ExplorerCollectionNodeChildren = ({
const pageData = {
meta: meta as DocMeta,
publicMode:
publicMode === PublicPageMode.Edgeless
publicMode === PublicDocMode.Edgeless
? ('edgeless' as const)
: publicMode === PublicPageMode.Page
: publicMode === PublicDocMode.Page
? ('page' as const)
: undefined,
favorite: favourites.some(fav => fav.id === meta.id),
@@ -14,7 +14,7 @@ import { GlobalContextService } from '@affine/core/modules/global-context';
import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { AffineDNDData } from '@affine/core/types/dnd';
import type { Collection } from '@affine/env/filter';
import { PublicPageMode } from '@affine/graphql';
import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import type { DocMeta } from '@blocksuite/affine/store';
@@ -283,9 +283,9 @@ const ExplorerCollectionNodeChildren = ({
const pageData = {
meta: meta as DocMeta,
publicMode:
publicMode === PublicPageMode.Edgeless
publicMode === PublicDocMode.Edgeless
? ('edgeless' as const)
: publicMode === PublicPageMode.Page
: publicMode === PublicDocMode.Page
? ('page' as const)
: undefined,
favorite: favourites.some(fav => fav.id === meta.id),
@@ -18,8 +18,7 @@ import type { GlobalCache } from '../../storage';
import type { WorkspaceService } from '../../workspace';
import type { ShareDocsStore } from '../stores/share-docs';
type ShareDocListType =
GetWorkspacePublicPagesQuery['workspace']['publicPages'];
type ShareDocListType = GetWorkspacePublicPagesQuery['workspace']['publicDocs'];
export const logger = new DebugLogger('affine:share-doc-list');
@@ -1,6 +1,6 @@
import type {
GetWorkspacePublicPageByIdQuery,
PublicPageMode,
PublicDocMode,
} from '@affine/graphql';
import {
backoffRetry,
@@ -20,7 +20,7 @@ import type { DocService } from '../../doc';
import type { WorkspaceService } from '../../workspace';
import type { ShareStore } from '../stores/share';
type ShareInfoType = GetWorkspacePublicPageByIdQuery['workspace']['publicPage'];
type ShareInfoType = GetWorkspacePublicPageByIdQuery['workspace']['publicDoc'];
export class ShareInfo extends Entity {
info$ = new LiveData<ShareInfoType | undefined | null>(null);
@@ -70,7 +70,7 @@ export class ShareInfo extends Entity {
return this.isRevalidating$.waitFor(v => v === false, signal);
}
async enableShare(mode: PublicPageMode) {
async enableShare(mode: PublicDocMode) {
await this.store.enableSharePage(
this.workspaceService.workspace.id,
this.docService.doc.id,
@@ -79,7 +79,7 @@ export class ShareInfo extends Entity {
await this.waitForRevalidation();
}
async changeShare(mode: PublicPageMode) {
async changeShare(mode: PublicDocMode) {
await this.enableShare(mode);
}
@@ -20,6 +20,6 @@ export class ShareDocsStore extends Store {
signal,
},
});
return data.workspace.publicPages;
return data.workspace.publicDocs;
}
}
@@ -1,4 +1,4 @@
import type { PublicPageMode } from '@affine/graphql';
import type { PublicDocMode } from '@affine/graphql';
import {
getWorkspacePublicPageByIdQuery,
publishPageMutation,
@@ -31,13 +31,13 @@ export class ShareStore extends Store {
signal,
},
});
return data.workspace.publicPage ?? undefined;
return data.workspace.publicDoc ?? undefined;
}
async enableSharePage(
workspaceId: string,
pageId: string,
docMode?: PublicPageMode,
docMode?: PublicDocMode,
signal?: AbortSignal
) {
if (!this.workspaceServerService.server) {
@@ -1,6 +1,6 @@
query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) {
workspace(id: $workspaceId) {
publicPage(pageId: $pageId) {
publicDoc(docId: $pageId) {
id
mode
}
@@ -1,6 +1,6 @@
query getWorkspacePublicPages($workspaceId: String!) {
workspace(id: $workspaceId) {
publicPages {
publicDocs {
id
mode
}
@@ -752,7 +752,7 @@ export const getWorkspacePublicPageByIdQuery = {
query: `
query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) {
workspace(id: $workspaceId) {
publicPage(pageId: $pageId) {
publicDoc(docId: $pageId) {
id
mode
}
@@ -768,7 +768,7 @@ export const getWorkspacePublicPagesQuery = {
query: `
query getWorkspacePublicPages($workspaceId: String!) {
workspace(id: $workspaceId) {
publicPages {
publicDocs {
id
mode
}
@@ -937,11 +937,11 @@ query prices {
export const publishPageMutation = {
id: 'publishPageMutation' as const,
operationName: 'publishPage',
definitionName: 'publishPage',
definitionName: 'publishDoc',
containsFile: false,
query: `
mutation publishPage($workspaceId: String!, $pageId: String!, $mode: PublicPageMode = Page) {
publishPage(workspaceId: $workspaceId, pageId: $pageId, mode: $mode) {
mutation publishPage($workspaceId: String!, $pageId: String!, $mode: PublicDocMode = Page) {
publishDoc(workspaceId: $workspaceId, docId: $pageId, mode: $mode) {
id
mode
}
@@ -1033,11 +1033,11 @@ mutation revokeMemberPermission($workspaceId: String!, $userId: String!) {
export const revokePublicPageMutation = {
id: 'revokePublicPageMutation' as const,
operationName: 'revokePublicPage',
definitionName: 'revokePublicPage',
definitionName: 'revokePublicDoc',
containsFile: false,
query: `
mutation revokePublicPage($workspaceId: String!, $pageId: String!) {
revokePublicPage(workspaceId: $workspaceId, pageId: $pageId) {
revokePublicDoc(workspaceId: $workspaceId, docId: $pageId) {
id
mode
public
@@ -1,9 +1,9 @@
mutation publishPage(
$workspaceId: String!
$pageId: String!
$mode: PublicPageMode = Page
$mode: PublicDocMode = Page
) {
publishPage(workspaceId: $workspaceId, pageId: $pageId, mode: $mode) {
publishDoc(workspaceId: $workspaceId, docId: $pageId, mode: $mode) {
id
mode
}
@@ -1,5 +1,5 @@
mutation revokePublicPage($workspaceId: String!, $pageId: String!) {
revokePublicPage(workspaceId: $workspaceId, pageId: $pageId) {
revokePublicDoc(workspaceId: $workspaceId, docId: $pageId) {
id
mode
public
+130 -118
View File
@@ -253,6 +253,23 @@ export interface DocNotFoundDataType {
spaceId: Scalars['String']['output'];
}
export interface DocPermissions {
__typename?: 'DocPermissions';
Doc_Copy: Scalars['Boolean']['output'];
Doc_Delete: Scalars['Boolean']['output'];
Doc_Duplicate: Scalars['Boolean']['output'];
Doc_Properties_Read: Scalars['Boolean']['output'];
Doc_Properties_Update: Scalars['Boolean']['output'];
Doc_Publish: Scalars['Boolean']['output'];
Doc_Read: Scalars['Boolean']['output'];
Doc_Restore: Scalars['Boolean']['output'];
Doc_TransferOwner: Scalars['Boolean']['output'];
Doc_Trash: Scalars['Boolean']['output'];
Doc_Update: Scalars['Boolean']['output'];
Doc_Users_Manage: Scalars['Boolean']['output'];
Doc_Users_Read: Scalars['Boolean']['output'];
}
/** User permission in doc */
export enum DocRole {
Editor = 'Editor',
@@ -264,10 +281,17 @@ export enum DocRole {
export interface DocType {
__typename?: 'DocType';
/** paginated doc granted users list */
grantedUsersList: PaginatedGrantedDocUserType;
id: Scalars['String']['output'];
permissions: RolePermissions;
mode: PublicDocMode;
permissions: DocPermissions;
public: Scalars['Boolean']['output'];
role: DocRole;
workspaceId: Scalars['String']['output'];
}
export interface DocTypeGrantedUsersListArgs {
pagination: PaginationInput;
}
export interface EditorType {
@@ -337,16 +361,18 @@ export enum ErrorNames {
COPILOT_SESSION_NOT_FOUND = 'COPILOT_SESSION_NOT_FOUND',
CUSTOMER_PORTAL_CREATE_FAILED = 'CUSTOMER_PORTAL_CREATE_FAILED',
DOC_ACCESS_DENIED = 'DOC_ACCESS_DENIED',
DOC_DEFAULT_ROLE_CAN_NOT_BE_OWNER = 'DOC_DEFAULT_ROLE_CAN_NOT_BE_OWNER',
DOC_HISTORY_NOT_FOUND = 'DOC_HISTORY_NOT_FOUND',
DOC_IS_NOT_PUBLIC = 'DOC_IS_NOT_PUBLIC',
DOC_NOT_FOUND = 'DOC_NOT_FOUND',
EARLY_ACCESS_REQUIRED = 'EARLY_ACCESS_REQUIRED',
EMAIL_ALREADY_USED = 'EMAIL_ALREADY_USED',
EMAIL_TOKEN_NOT_FOUND = 'EMAIL_TOKEN_NOT_FOUND',
EMAIL_VERIFICATION_REQUIRED = 'EMAIL_VERIFICATION_REQUIRED',
EXPECT_TO_GRANT_DOC_USER_ROLES = 'EXPECT_TO_GRANT_DOC_USER_ROLES',
EXPECT_TO_PUBLISH_PAGE = 'EXPECT_TO_PUBLISH_PAGE',
EXPECT_TO_PUBLISH_DOC = 'EXPECT_TO_PUBLISH_DOC',
EXPECT_TO_REVOKE_DOC_USER_ROLES = 'EXPECT_TO_REVOKE_DOC_USER_ROLES',
EXPECT_TO_REVOKE_PUBLIC_PAGE = 'EXPECT_TO_REVOKE_PUBLIC_PAGE',
EXPECT_TO_REVOKE_PUBLIC_DOC = 'EXPECT_TO_REVOKE_PUBLIC_DOC',
EXPECT_TO_UPDATE_DOC_USER_ROLE = 'EXPECT_TO_UPDATE_DOC_USER_ROLE',
FAILED_TO_CHECKOUT = 'FAILED_TO_CHECKOUT',
FAILED_TO_SAVE_UPDATES = 'FAILED_TO_SAVE_UPDATES',
@@ -375,7 +401,6 @@ export enum ErrorNames {
NO_COPILOT_PROVIDER_AVAILABLE = 'NO_COPILOT_PROVIDER_AVAILABLE',
OAUTH_ACCOUNT_ALREADY_CONNECTED = 'OAUTH_ACCOUNT_ALREADY_CONNECTED',
OAUTH_STATE_EXPIRED = 'OAUTH_STATE_EXPIRED',
PAGE_IS_NOT_PUBLIC = 'PAGE_IS_NOT_PUBLIC',
PASSWORD_REQUIRED = 'PASSWORD_REQUIRED',
QUERY_TOO_LONG = 'QUERY_TOO_LONG',
RUNTIME_CONFIG_NOT_FOUND = 'RUNTIME_CONFIG_NOT_FOUND',
@@ -453,23 +478,16 @@ export interface GrantDocUserRolesInput {
workspaceId: Scalars['String']['input'];
}
export interface GrantedDocUserEdge {
__typename?: 'GrantedDocUserEdge';
cursor: Scalars['String']['output'];
user: GrantedDocUserType;
}
export interface GrantedDocUserType {
__typename?: 'GrantedDocUserType';
role: DocRole;
user: UserType;
user: PublicUserType;
}
export interface GrantedDocUsersConnection {
__typename?: 'GrantedDocUsersConnection';
edges: Array<GrantedDocUserEdge>;
pageInfo: PageInfo;
totalCount: Scalars['Int']['output'];
export interface GrantedDocUserTypeEdge {
__typename?: 'GrantedDocUserTypeEdge';
cursor: Scalars['String']['output'];
node: GrantedDocUserType;
}
export interface InvalidEmailDataType {
@@ -688,7 +706,9 @@ export interface Mutation {
invite: Scalars['String']['output'];
inviteBatch: Array<InviteResult>;
leaveWorkspace: Scalars['Boolean']['output'];
publishPage: WorkspacePage;
publishDoc: DocType;
/** @deprecated use publishDoc instead */
publishPage: DocType;
recoverDoc: Scalars['DateTime']['output'];
releaseDeletedBlobs: Scalars['Boolean']['output'];
/** Remove user avatar */
@@ -698,21 +718,20 @@ export interface Mutation {
revoke: Scalars['Boolean']['output'];
revokeDocUserRoles: Scalars['Boolean']['output'];
revokeInviteLink: Scalars['Boolean']['output'];
/** @deprecated use revokePublicPage */
revokePage: Scalars['Boolean']['output'];
revokePublicPage: WorkspacePage;
revokePublicDoc: DocType;
/** @deprecated use revokePublicDoc instead */
revokePublicPage: DocType;
sendChangeEmail: Scalars['Boolean']['output'];
sendChangePasswordEmail: Scalars['Boolean']['output'];
sendSetPasswordEmail: Scalars['Boolean']['output'];
sendVerifyChangeEmail: Scalars['Boolean']['output'];
sendVerifyEmail: Scalars['Boolean']['output'];
setBlob: Scalars['String']['output'];
/** @deprecated renamed to publishPage */
sharePage: Scalars['Boolean']['output'];
/** Update a copilot prompt */
updateCopilotPrompt: CopilotPromptType;
/** Update a chat session */
updateCopilotSession: Scalars['String']['output'];
updateDocDefaultRole: Scalars['Boolean']['output'];
updateDocUserRole: Scalars['Boolean']['output'];
updateProfile: UserType;
/** update server runtime configurable setting */
@@ -867,8 +886,14 @@ export interface MutationLeaveWorkspaceArgs {
workspaceName?: InputMaybe<Scalars['String']['input']>;
}
export interface MutationPublishDocArgs {
docId: Scalars['String']['input'];
mode?: InputMaybe<PublicDocMode>;
workspaceId: Scalars['String']['input'];
}
export interface MutationPublishPageArgs {
mode?: InputMaybe<PublicPageMode>;
mode?: InputMaybe<PublicDocMode>;
pageId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
}
@@ -900,21 +925,20 @@ export interface MutationRevokeArgs {
}
export interface MutationRevokeDocUserRolesArgs {
docId: Scalars['String']['input'];
userIds: Array<Scalars['String']['input']>;
input: RevokeDocUserRolesInput;
}
export interface MutationRevokeInviteLinkArgs {
workspaceId: Scalars['String']['input'];
}
export interface MutationRevokePageArgs {
pageId: Scalars['String']['input'];
export interface MutationRevokePublicDocArgs {
docId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
}
export interface MutationRevokePublicPageArgs {
pageId: Scalars['String']['input'];
docId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
}
@@ -948,11 +972,6 @@ export interface MutationSetBlobArgs {
workspaceId: Scalars['String']['input'];
}
export interface MutationSharePageArgs {
pageId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
}
export interface MutationUpdateCopilotPromptArgs {
messages: Array<CopilotPromptMessageInput>;
name: Scalars['String']['input'];
@@ -962,10 +981,12 @@ export interface MutationUpdateCopilotSessionArgs {
options: UpdateChatSessionInput;
}
export interface MutationUpdateDocDefaultRoleArgs {
input: UpdateDocDefaultRoleInput;
}
export interface MutationUpdateDocUserRoleArgs {
docId: Scalars['String']['input'];
role: DocRole;
userId: Scalars['String']['input'];
input: UpdateDocUserRoleInput;
}
export interface MutationUpdateProfileArgs {
@@ -1021,15 +1042,6 @@ export enum OAuthProviderType {
OIDC = 'OIDC',
}
export interface PageGrantedUsersInput {
/** Cursor */
after?: InputMaybe<Scalars['String']['input']>;
/** Cursor */
before?: InputMaybe<Scalars['String']['input']>;
first: Scalars['Int']['input'];
offset: Scalars['Int']['input'];
}
export interface PageInfo {
__typename?: 'PageInfo';
endCursor: Maybe<Scalars['String']['output']>;
@@ -1038,6 +1050,22 @@ export interface PageInfo {
startCursor: Maybe<Scalars['String']['output']>;
}
export interface PaginatedGrantedDocUserType {
__typename?: 'PaginatedGrantedDocUserType';
edges: Array<GrantedDocUserTypeEdge>;
pageInfo: PageInfo;
totalCount: Scalars['Int']['output'];
}
export interface PaginationInput {
/** returns the elements in the list that come after the specified cursor. */
after?: InputMaybe<Scalars['String']['input']>;
/** returns the first n elements from the list. */
first?: InputMaybe<Scalars['Int']['input']>;
/** ignore the first n elements from the list. */
offset?: InputMaybe<Scalars['Int']['input']>;
}
export interface PasswordLimitsType {
__typename?: 'PasswordLimitsType';
maxLength: Scalars['Int']['output'];
@@ -1052,12 +1080,20 @@ export enum Permission {
Owner = 'Owner',
}
/** The mode which the public page default in */
export enum PublicPageMode {
/** The mode which the public doc default in */
export enum PublicDocMode {
Edgeless = 'Edgeless',
Page = 'Page',
}
export interface PublicUserType {
__typename?: 'PublicUserType';
avatarUrl: Maybe<Scalars['String']['output']>;
email: Scalars['String']['output'];
id: Scalars['String']['output'];
name: Scalars['String']['output'];
}
export interface Query {
__typename?: 'Query';
/** @deprecated use `user.quotaUsage` instead */
@@ -1166,34 +1202,10 @@ export interface RemoveAvatar {
success: Scalars['Boolean']['output'];
}
export interface RolePermissions {
__typename?: 'RolePermissions';
Doc_Copy: Scalars['Boolean']['output'];
Doc_Delete: Scalars['Boolean']['output'];
Doc_Duplicate: Scalars['Boolean']['output'];
Doc_Properties_Read: Scalars['Boolean']['output'];
Doc_Properties_Update: Scalars['Boolean']['output'];
Doc_Publish: Scalars['Boolean']['output'];
Doc_Read: Scalars['Boolean']['output'];
Doc_Restore: Scalars['Boolean']['output'];
Doc_TransferOwner: Scalars['Boolean']['output'];
Doc_Trash: Scalars['Boolean']['output'];
Doc_Update: Scalars['Boolean']['output'];
Doc_Users_Manage: Scalars['Boolean']['output'];
Doc_Users_Read: Scalars['Boolean']['output'];
Workspace_CreateDoc: Scalars['Boolean']['output'];
Workspace_Delete: Scalars['Boolean']['output'];
Workspace_Organize_Read: Scalars['Boolean']['output'];
Workspace_Properties_Create: Scalars['Boolean']['output'];
Workspace_Properties_Delete: Scalars['Boolean']['output'];
Workspace_Properties_Read: Scalars['Boolean']['output'];
Workspace_Properties_Update: Scalars['Boolean']['output'];
Workspace_Settings_Read: Scalars['Boolean']['output'];
Workspace_Settings_Update: Scalars['Boolean']['output'];
Workspace_Sync: Scalars['Boolean']['output'];
Workspace_TransferOwner: Scalars['Boolean']['output'];
Workspace_Users_Manage: Scalars['Boolean']['output'];
Workspace_Users_Read: Scalars['Boolean']['output'];
export interface RevokeDocUserRolesInput {
docId: Scalars['String']['input'];
userIds: Array<Scalars['String']['input']>;
workspaceId: Scalars['String']['input'];
}
export interface RuntimeConfigNotFoundDataType {
@@ -1395,6 +1407,19 @@ export interface UpdateChatSessionInput {
sessionId: Scalars['String']['input'];
}
export interface UpdateDocDefaultRoleInput {
docId: Scalars['String']['input'];
role: DocRole;
workspaceId: Scalars['String']['input'];
}
export interface UpdateDocUserRoleInput {
docId: Scalars['String']['input'];
role: DocRole;
userId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
}
export interface UpdateUserInput {
/** User name */
name?: InputMaybe<Scalars['String']['input']>;
@@ -1514,14 +1539,6 @@ export interface WorkspaceMembersExceedLimitToDowngradeDataType {
limit: Scalars['Int']['output'];
}
export interface WorkspacePage {
__typename?: 'WorkspacePage';
id: Scalars['String']['output'];
mode: PublicPageMode;
public: Scalars['Boolean']['output'];
workspaceId: Scalars['String']['output'];
}
export interface WorkspacePageMeta {
__typename?: 'WorkspacePageMeta';
createdAt: Scalars['DateTime']['output'];
@@ -1591,6 +1608,8 @@ export interface WorkspaceType {
blobsSize: Scalars['Int']['output'];
/** Workspace created date */
createdAt: Scalars['DateTime']['output'];
/** Get get with given id */
doc: DocType;
/** Enable AI */
enableAi: Scalars['Boolean']['output'];
/** Enable url previous when sharing */
@@ -1612,33 +1631,35 @@ export interface WorkspaceType {
members: Array<InviteUserType>;
/** Owner of workspace */
owner: UserType;
/** Page granted users list */
pageGrantedUsersList: GrantedDocUsersConnection;
/** Cloud page metadata of workspace */
pageMeta: WorkspacePageMeta;
/** Check if current user has permission to access the page */
pagePermission: DocType;
/** is Public workspace */
public: Scalars['Boolean']['output'];
/** Get public page of a workspace by page id. */
publicPage: Maybe<WorkspacePage>;
/** Public pages of a workspace */
publicPages: Array<WorkspacePage>;
publicDoc: Maybe<DocType>;
/** Get public docs of a workspace */
publicDocs: Array<DocType>;
/**
* Get public page of a workspace by page id.
* @deprecated use [WorkspaceType.publicDoc] instead
*/
publicPage: Maybe<DocType>;
/** @deprecated use [WorkspaceType.publicDocs] instead */
publicPages: Array<DocType>;
/** quota of workspace */
quota: WorkspaceQuotaType;
/** Role of current signed in user in workspace */
role: Permission;
/**
* Shared pages of workspace
* @deprecated use WorkspaceType.publicPages
*/
sharedPages: Array<Scalars['String']['output']>;
/** The team subscription of the workspace, if exists. */
subscription: Maybe<SubscriptionType>;
/** if workspace is team workspace */
team: Scalars['Boolean']['output'];
}
export interface WorkspaceTypeDocArgs {
docId: Scalars['String']['input'];
}
export interface WorkspaceTypeHistoriesArgs {
before?: InputMaybe<Scalars['DateTime']['input']>;
guid: Scalars['String']['input'];
@@ -1656,17 +1677,12 @@ export interface WorkspaceTypeMembersArgs {
take?: InputMaybe<Scalars['Int']['input']>;
}
export interface WorkspaceTypePageGrantedUsersListArgs {
pageGrantedUsersInput: PageGrantedUsersInput;
pageId: Scalars['String']['input'];
}
export interface WorkspaceTypePageMetaArgs {
pageId: Scalars['String']['input'];
}
export interface WorkspaceTypePagePermissionArgs {
pageId: Scalars['String']['input'];
export interface WorkspaceTypePublicDocArgs {
docId: Scalars['String']['input'];
}
export interface WorkspaceTypePublicPageArgs {
@@ -2357,10 +2373,10 @@ export type GetWorkspacePublicPageByIdQuery = {
__typename?: 'Query';
workspace: {
__typename?: 'WorkspaceType';
publicPage: {
__typename?: 'WorkspacePage';
publicDoc: {
__typename?: 'DocType';
id: string;
mode: PublicPageMode;
mode: PublicDocMode;
} | null;
};
};
@@ -2373,10 +2389,10 @@ export type GetWorkspacePublicPagesQuery = {
__typename?: 'Query';
workspace: {
__typename?: 'WorkspaceType';
publicPages: Array<{
__typename?: 'WorkspacePage';
publicDocs: Array<{
__typename?: 'DocType';
id: string;
mode: PublicPageMode;
mode: PublicDocMode;
}>;
};
};
@@ -2527,16 +2543,12 @@ export type PricesQuery = {
export type PublishPageMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
pageId: Scalars['String']['input'];
mode?: InputMaybe<PublicPageMode>;
mode?: InputMaybe<PublicDocMode>;
}>;
export type PublishPageMutation = {
__typename?: 'Mutation';
publishPage: {
__typename?: 'WorkspacePage';
id: string;
mode: PublicPageMode;
};
publishDoc: { __typename?: 'DocType'; id: string; mode: PublicDocMode };
};
export type QuotaQueryVariables = Exact<{ [key: string]: never }>;
@@ -2618,10 +2630,10 @@ export type RevokePublicPageMutationVariables = Exact<{
export type RevokePublicPageMutation = {
__typename?: 'Mutation';
revokePublicPage: {
__typename?: 'WorkspacePage';
revokePublicDoc: {
__typename?: 'DocType';
id: string;
mode: PublicPageMode;
mode: PublicDocMode;
public: boolean;
};
};