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

View File

@@ -25,16 +25,16 @@ model User {
features UserFeature[] features UserFeature[]
userStripeCustomer UserStripeCustomer? userStripeCustomer UserStripeCustomer?
workspacePermissions WorkspaceUserPermission[] workspacePermissions WorkspaceUserPermission[]
pagePermissions WorkspacePageUserPermission[] docPermissions WorkspaceDocUserPermission[]
connectedAccounts ConnectedAccount[] connectedAccounts ConnectedAccount[]
sessions UserSession[] sessions UserSession[]
aiSessions AiSession[] aiSessions AiSession[]
updatedRuntimeConfigs RuntimeConfig[] updatedRuntimeConfigs RuntimeConfig[]
userSnapshots UserSnapshot[] userSnapshots UserSnapshot[]
createdSnapshot Snapshot[] @relation("createdSnapshot") createdSnapshot Snapshot[] @relation("createdSnapshot")
updatedSnapshot Snapshot[] @relation("updatedSnapshot") updatedSnapshot Snapshot[] @relation("updatedSnapshot")
createdUpdate Update[] @relation("createdUpdate") createdUpdate Update[] @relation("createdUpdate")
createdHistory SnapshotHistory[] @relation("createdHistory") createdHistory SnapshotHistory[] @relation("createdHistory")
@@index([email]) @@index([email])
@@map("users") @@map("users")
@@ -102,11 +102,11 @@ model Workspace {
enableAi Boolean @default(true) @map("enable_ai") enableAi Boolean @default(true) @map("enable_ai")
enableUrlPreview Boolean @default(false) @map("enable_url_preview") enableUrlPreview Boolean @default(false) @map("enable_url_preview")
features WorkspaceFeature[] features WorkspaceFeature[]
pages WorkspacePage[] docs WorkspaceDoc[]
permissions WorkspaceUserPermission[] permissions WorkspaceUserPermission[]
pagePermissions WorkspacePageUserPermission[] docPermissions WorkspaceDocUserPermission[]
blobs Blob[] blobs Blob[]
@@map("workspaces") @@map("workspaces")
} }
@@ -116,18 +116,18 @@ model Workspace {
// We won't make sure every page has a corresponding record in this table. // 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, // 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. // and for others we will make sure it's has a default value return in our business logic.
model WorkspacePage { model WorkspaceDoc {
workspaceId String @map("workspace_id") @db.VarChar workspaceId String @map("workspace_id") @db.VarChar
pageId String @map("page_id") @db.VarChar docId String @map("page_id") @db.VarChar
public Boolean @default(false) public Boolean @default(false)
// Workspace user's default role in this page, default is `Manager` // 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 // Page/Edgeless
mode Int @default(0) @db.SmallInt mode Int @default(0) @db.SmallInt
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
@@id([workspaceId, pageId]) @@id([workspaceId, docId])
@@map("workspace_pages") @@map("workspace_pages")
} }
@@ -162,9 +162,9 @@ model WorkspaceUserPermission {
@@map("workspace_user_permissions") @@map("workspace_user_permissions")
} }
model WorkspacePageUserPermission { model WorkspaceDocUserPermission {
workspaceId String @map("workspace_id") @db.VarChar 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 userId String @map("user_id") @db.VarChar
// External/Reader/Editor/Manager/Owner // External/Reader/Editor/Manager/Owner
type Int @db.SmallInt type Int @db.SmallInt
@@ -173,7 +173,7 @@ model WorkspacePageUserPermission {
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
workspace Workspace @relation(fields: [workspaceId], 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") @@map("workspace_page_user_permissions")
} }

View File

@@ -49,7 +49,7 @@ test.after(async t => {
test('should create page with default mode and public false', async t => { test('should create page with default mode and public false', async t => {
const page = await t.context.page.upsert(workspace.id, 'page1'); const page = await t.context.page.upsert(workspace.id, 'page1');
t.is(page.workspaceId, workspace.id); 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.mode, PublicPageMode.Page);
t.is(page.public, false); 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'); await t.context.page.upsert(workspace.id, 'page3');
const pages = await t.context.page.findPublics(workspace.id); const pages = await t.context.page.findPublics(workspace.id);
t.is(pages.length, 2); 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 => { test('should grant a member to access a page', async t => {

View File

@@ -23,6 +23,7 @@ import {
createInviteLink, createInviteLink,
createTestingApp, createTestingApp,
createWorkspace, createWorkspace,
docGrantedUsersList,
getInviteInfo, getInviteInfo,
getInviteLink, getInviteLink,
getWorkspace, getWorkspace,
@@ -31,7 +32,6 @@ import {
inviteUser, inviteUser,
inviteUsers, inviteUsers,
leaveWorkspace, leaveWorkspace,
pageGrantedUsersList,
revokeDocUserRoles, revokeDocUserRoles,
revokeInviteLink, revokeInviteLink,
revokeMember, revokeMember,
@@ -39,7 +39,7 @@ import {
signUp, signUp,
sleep, sleep,
TestingApp, TestingApp,
updatePageDefaultRole, updateDocDefaultRole,
UserAuthedType, UserAuthedType,
} from './utils'; } from './utils';
@@ -817,13 +817,13 @@ test('should be able to grant and revoke users role in page', async t => {
read, read,
external, external,
} = await init(app, 5); } = await init(app, 5);
const pageId = nanoid(); const docId = nanoid();
const res = await grantDocUserRoles( const res = await grantDocUserRoles(
app, app,
admin.token.token, admin.token.token,
ws.id, ws.id,
pageId, docId,
[read.id, write.id], [read.id, write.id],
DocRole.Manager DocRole.Manager
); );
@@ -840,7 +840,7 @@ test('should be able to grant and revoke users role in page', async t => {
app, app,
admin.token.token, admin.token.token,
ws.id, ws.id,
pageId, docId,
[read.id], [read.id],
DocRole.Reader DocRole.Reader
); );
@@ -849,7 +849,7 @@ test('should be able to grant and revoke users role in page', async t => {
app, app,
read.token.token, read.token.token,
ws.id, ws.id,
pageId, docId,
[external.id], [external.id],
DocRole.Editor 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, app,
admin.token.token, admin.token.token,
ws.id, ws.id,
pageId docId
); );
t.is(pageUsersList.data.workspace.pageGrantedUsersList.totalCount, 3); t.is(docUsersList.data.workspace.doc.grantedUsersList.totalCount, 3);
const externalRole = const externalRole =
pageUsersList.data.workspace.pageGrantedUsersList.edges.find( docUsersList.data.workspace.doc.grantedUsersList.edges.find(
(edge: any) => edge.node.user.id === external.id (edge: any) => edge.node.user.id === external.id
)?.node.role; )?.node.role;
t.is(externalRole, DocRole[DocRole.Editor]); 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 => { test('should be able to change the default role in page', async t => {
const { app } = t.context; const { app } = t.context;
const { teamWorkspace: ws, admin } = await init(app, 5); const { teamWorkspace: ws, admin } = await init(app, 5);
const pageId = nanoid(); const docId = nanoid();
const res = await updatePageDefaultRole( const res = await updateDocDefaultRole(
app, app,
admin.token.token, admin.token.token,
ws.id, ws.id,
pageId, docId,
DocRole.Reader DocRole.Reader
); );
t.deepEqual(res.body, { t.deepEqual(res.body, {
data: { data: {
updatePageDefaultRole: true, updateDocDefaultRole: true,
}, },
}); });
}); });
@@ -902,53 +902,53 @@ test('default page role should be able to override the workspace role', async t
external, external,
} = await init(app, 5); } = await init(app, 5);
const pageId = nanoid(); const docId = nanoid();
const res = await updatePageDefaultRole( const res = await updateDocDefaultRole(
app, app,
admin.token.token, admin.token.token,
workspace.id, workspace.id,
pageId, docId,
DocRole.Manager DocRole.Manager
); );
t.deepEqual(res.body, { t.deepEqual(res.body, {
data: { data: {
updatePageDefaultRole: true, updateDocDefaultRole: true,
}, },
}); });
// reader can manage the page if the page default role is Manager // reader can manage the page if the page default role is Manager
{ {
const readerRes = await updatePageDefaultRole( const readerRes = await updateDocDefaultRole(
app, app,
read.token.token, read.token.token,
workspace.id, workspace.id,
pageId, docId,
DocRole.Manager DocRole.Manager
); );
t.deepEqual(readerRes.body, { t.deepEqual(readerRes.body, {
data: { data: {
updatePageDefaultRole: true, updateDocDefaultRole: true,
}, },
}); });
} }
// external can't manage the page even if the page default role is Manager // external can't manage the page even if the page default role is Manager
{ {
const externalRes = await updatePageDefaultRole( const externalRes = await updateDocDefaultRole(
app, app,
external.token.token, external.token.token,
workspace.id, workspace.id,
pageId, docId,
DocRole.Manager DocRole.Manager
); );
t.like(externalRes.body, { t.like(externalRes.body, {
errors: [ 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 => { test('should be able to grant and revoke doc user role', async t => {
const { app } = t.context; const { app } = t.context;
const { teamWorkspace: ws, admin, read, external } = await init(app, 5); const { teamWorkspace: ws, admin, read, external } = await init(app, 5);
const pageId = nanoid(); const docId = nanoid();
const res = await grantDocUserRoles( const res = await grantDocUserRoles(
app, app,
admin.token.token, admin.token.token,
ws.id, ws.id,
pageId, docId,
[external.id], [external.id],
DocRole.Manager DocRole.Manager
); );
@@ -981,7 +981,7 @@ test('should be able to grant and revoke doc user role', async t => {
app, app,
external.token.token, external.token.token,
ws.id, ws.id,
pageId, docId,
[read.id], [read.id],
DocRole.Manager DocRole.Manager
); );
@@ -999,7 +999,7 @@ test('should be able to grant and revoke doc user role', async t => {
app, app,
admin.token.token, admin.token.token,
ws.id, ws.id,
pageId, docId,
external.id external.id
); );
@@ -1014,14 +1014,14 @@ test('should be able to grant and revoke doc user role', async t => {
app, app,
external.token.token, external.token.token,
ws.id, ws.id,
pageId, docId,
read.id read.id
); );
t.like(externalRes.body, { t.like(externalRes.body, {
errors: [ 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 => { test('update page default role should throw error if the space does not exist', async t => {
const { app } = t.context; const { app } = t.context;
const { admin } = await init(app, 5); const { admin } = await init(app, 5);
const pageId = nanoid(); const docId = nanoid();
const nonExistWorkspaceId = 'non-exist-workspace'; const nonExistWorkspaceId = 'non-exist-workspace';
const res = await request(app.getHttpServer()) const res = await request(app.getHttpServer())
.post('/graphql') .post('/graphql')
@@ -1040,9 +1040,9 @@ test('update page default role should throw error if the space does not exist',
.send({ .send({
query: ` query: `
mutation { mutation {
updatePageDefaultRole(input: { updateDocDefaultRole(input: {
workspaceId: "${nonExistWorkspaceId}", workspaceId: "${nonExistWorkspaceId}",
docId: "${pageId}", docId: "${docId}",
role: Manager, role: Manager,
}) })
} }
@@ -1052,7 +1052,7 @@ test('update page default role should throw error if the space does not exist',
t.like(res.body, { t.like(res.body, {
errors: [ 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}.`,
}, },
], ],
}); });

View File

@@ -54,7 +54,7 @@ export function revokeDocUserRoles(
}); });
} }
export function updatePageDefaultRole( export function updateDocDefaultRole(
app: INestApplication, app: INestApplication,
token: string, token: string,
workspaceId: string, workspaceId: string,
@@ -68,7 +68,7 @@ export function updatePageDefaultRole(
.send({ .send({
query: ` query: `
mutation { mutation {
updatePageDefaultRole(input: { updateDocDefaultRole(input: {
workspaceId: "${workspaceId}", workspaceId: "${workspaceId}",
docId: "${docId}", docId: "${docId}",
role: ${DocRole[role]} role: ${DocRole[role]}
@@ -78,7 +78,7 @@ export function updatePageDefaultRole(
}); });
} }
export async function pageGrantedUsersList( export async function docGrantedUsersList(
app: INestApplication, app: INestApplication,
token: string, token: string,
workspaceId: string, workspaceId: string,
@@ -94,14 +94,16 @@ export async function pageGrantedUsersList(
query: ` query: `
query { query {
workspace(id: "${workspaceId}") { workspace(id: "${workspaceId}") {
pageGrantedUsersList(pageId: "${docId}", pagination: { first: ${first}, offset: ${offset} }) { doc(docId: "${docId}") {
totalCount grantedUsersList(pagination: { first: ${first}, offset: ${offset} }) {
edges { totalCount
edges {
cursor cursor
node { node {
role role
user { user {
id id
}
} }
} }
} }

View File

@@ -103,11 +103,11 @@ export async function updateWorkspace(
return res.body.data.updateWorkspace.public; return res.body.data.updateWorkspace.public;
} }
export async function publishPage( export async function publishDoc(
app: INestApplication, app: INestApplication,
token: string, token: string,
workspaceId: string, workspaceId: string,
pageId: string docId: string
) { ) {
const res = await request(app.getHttpServer()) const res = await request(app.getHttpServer())
.post(gql) .post(gql)
@@ -116,7 +116,7 @@ export async function publishPage(
.send({ .send({
query: ` query: `
mutation { mutation {
publishPage(workspaceId: "${workspaceId}", pageId: "${pageId}") { publishDoc(workspaceId: "${workspaceId}", docId: "${docId}") {
id id
mode mode
} }
@@ -124,14 +124,14 @@ export async function publishPage(
`, `,
}) })
.expect(200); .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, app: INestApplication,
token: string, token: string,
workspaceId: string, workspaceId: string,
pageId: string docId: string
) { ) {
const res = await request(app.getHttpServer()) const res = await request(app.getHttpServer())
.post(gql) .post(gql)
@@ -140,7 +140,7 @@ export async function revokePublicPage(
.send({ .send({
query: ` query: `
mutation { mutation {
revokePublicPage(workspaceId: "${workspaceId}", pageId: "${pageId}") { revokePublicDoc(workspaceId: "${workspaceId}", docId: "${docId}") {
id id
mode mode
public public
@@ -149,7 +149,7 @@ export async function revokePublicPage(
`, `,
}) })
.expect(200); .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( export async function grantMember(

View File

@@ -10,8 +10,8 @@ import {
createWorkspace, createWorkspace,
getWorkspacePublicPages, getWorkspacePublicPages,
inviteUser, inviteUser,
publishPage, publishDoc,
revokePublicPage, revokePublicDoc,
signUp, signUp,
TestingApp, TestingApp,
updateWorkspace, updateWorkspace,
@@ -83,8 +83,8 @@ test('should share a page', async t => {
const workspace = await createWorkspace(app, u1.token.token); const workspace = await createWorkspace(app, u1.token.token);
const share = await publishPage(app, u1.token.token, workspace.id, 'page1'); const share = await publishDoc(app, u1.token.token, workspace.id, 'doc1');
t.is(share.id, 'page1', 'failed to share page'); t.is(share.id, 'doc1', 'failed to share doc');
const pages = await getWorkspacePublicPages( const pages = await getWorkspacePublicPages(
app, app,
u1.token.token, 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.is(pages.length, 1, 'failed to get shared pages');
t.deepEqual( t.deepEqual(
pages[0], pages[0],
{ id: 'page1', mode: 'Page' }, { id: 'doc1', mode: 'Page' },
'failed to get shared page: page1' 'failed to get shared doc: doc1'
); );
const resp1 = await request(app.getHttpServer()) 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'); t.is(resp2.statusCode, 200, 'failed to get root doc with public pages');
const resp3 = await request(app.getHttpServer()) 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' }); .auth(u1.token.token, { type: 'bearer' });
// 404 because we don't put the page doc to server // 404 because we don't put the page doc to server
t.is(resp3.statusCode, 404, 'failed to get shared doc with u1 token'); t.is(resp3.statusCode, 404, 'failed to get shared doc with u1 token');
const resp4 = await request(app.getHttpServer()).get( 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 // 404 because we don't put the page doc to server
t.is(resp4.statusCode, 404, 'should not get shared doc without token'); 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( t.is(
msg1, msg1,
'You do not have permission to access doc page2 under Space not_exists_ws.', 'You do not have permission to access doc doc2 under Space not_exists_ws.',
'unauthorized user can share page' 'unauthorized user can share doc'
); );
const msg2 = await revokePublicPage( const msg2 = await revokePublicDoc(
app, app,
u2.token.token, u2.token.token,
'not_exists_ws', 'not_exists_ws',
'page2' 'doc2'
); );
t.is( t.is(
msg2, msg2,
'You do not have permission to access doc page2 under Space not_exists_ws.', 'You do not have permission to access doc doc2 under Space not_exists_ws.',
'unauthorized user can share page' 'unauthorized user can share doc'
); );
const revoke = await revokePublicPage( const revoke = await revokePublicDoc(
app, app,
u1.token.token, u1.token.token,
workspace.id, workspace.id,
'page1' 'doc1'
); );
t.false(revoke.public, 'failed to revoke page'); t.false(revoke.public, 'failed to revoke doc');
const pages2 = await getWorkspacePublicPages( const pages2 = await getWorkspacePublicPages(
app, app,
u1.token.token, u1.token.token,
workspace.id workspace.id
); );
t.is(pages2.length, 0, 'failed to get shared pages'); t.is(pages2.length, 0, 'failed to get shared pages');
const msg4 = await revokePublicPage( const msg4 = await revokePublicDoc(app, u1.token.token, workspace.id, 'doc3');
app, t.is(msg4, 'Doc is not public');
u1.token.token,
workspace.id,
'page3'
);
t.is(msg4, 'Page is not public');
const pages3 = await getWorkspacePublicPages( const pages3 = await getWorkspacePublicPages(
app, app,

View File

@@ -41,7 +41,7 @@ test.before(async t => {
t.context.storage = app.get(WorkspaceBlobStorage); t.context.storage = app.get(WorkspaceBlobStorage);
t.context.workspace = app.get(PgWorkspaceDocStorageAdapter); t.context.workspace = app.get(PgWorkspaceDocStorageAdapter);
await db.workspacePage.create({ await db.workspaceDoc.create({
data: { data: {
workspace: { workspace: {
create: { create: {
@@ -49,12 +49,12 @@ test.before(async t => {
public: true, public: true,
}, },
}, },
pageId: 'private', docId: 'private',
public: false, public: false,
}, },
}); });
await db.workspacePage.create({ await db.workspaceDoc.create({
data: { data: {
workspace: { workspace: {
create: { create: {
@@ -62,12 +62,12 @@ test.before(async t => {
public: false, public: false,
}, },
}, },
pageId: 'public', docId: 'public',
public: true, public: true,
}, },
}); });
await db.workspacePage.create({ await db.workspaceDoc.create({
data: { data: {
workspace: { workspace: {
create: { create: {
@@ -75,7 +75,7 @@ test.before(async t => {
public: false, public: false,
}, },
}, },
pageId: 'private', docId: 'private',
public: false, 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.status, HttpStatus.OK);
t.is(res.get('publish-mode'), 'page'); t.is(res.get('publish-mode'), 'page');
await db.workspacePage.update({ await db.workspaceDoc.update({
where: { workspaceId_pageId: { workspaceId: 'private', pageId: 'public' } }, where: { workspaceId_docId: { workspaceId: 'private', docId: 'public' } },
data: { mode: 1 }, data: { mode: 1 },
}); });

View File

@@ -428,13 +428,13 @@ export const USER_FRIENDLY_ERRORS = {
message: ({ spaceId, blobId }) => message: ({ spaceId, blobId }) =>
`Blob ${blobId} not found in Space ${spaceId}.`, `Blob ${blobId} not found in Space ${spaceId}.`,
}, },
expect_to_publish_page: { expect_to_publish_doc: {
type: 'invalid_input', 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', 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: { expect_to_grant_doc_user_roles: {
type: 'invalid_input', type: 'invalid_input',
@@ -454,9 +454,9 @@ export const USER_FRIENDLY_ERRORS = {
message: ({ spaceId, docId }) => message: ({ spaceId, docId }) =>
`Expect update roles on doc ${docId} under Space ${spaceId}, not a Space.`, `Expect update roles on doc ${docId} under Space ${spaceId}, not a Space.`,
}, },
page_is_not_public: { doc_is_not_public: {
type: 'bad_request', type: 'bad_request',
message: 'Page is not public.', message: 'Doc is not public.',
}, },
failed_to_save_updates: { failed_to_save_updates: {
type: 'internal_server_error', type: 'internal_server_error',
@@ -470,13 +470,13 @@ export const USER_FRIENDLY_ERRORS = {
type: 'action_forbidden', type: 'action_forbidden',
message: 'A Team workspace is required to perform this action.', 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', 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', type: 'invalid_input',
message: 'Can not batch grant page owner permissions.', message: 'Can not batch grant doc owner permissions.',
}, },
// Subscription Errors // Subscription Errors

View File

@@ -337,15 +337,15 @@ export class BlobNotFound extends UserFriendlyError {
} }
} }
export class ExpectToPublishPage extends UserFriendlyError { export class ExpectToPublishDoc extends UserFriendlyError {
constructor(message?: string) { 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) { constructor(message?: string) {
super('invalid_input', 'expect_to_revoke_public_page', message); super('invalid_input', 'expect_to_revoke_public_doc', message);
} }
} }
@ObjectType() @ObjectType()
@@ -382,9 +382,9 @@ export class ExpectToUpdateDocUserRole extends UserFriendlyError {
} }
} }
export class PageIsNotPublic extends UserFriendlyError { export class DocIsNotPublic extends UserFriendlyError {
constructor(message?: string) { 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) { 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) { 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() @ObjectType()
@@ -764,17 +764,17 @@ export enum ErrorNames {
INVALID_HISTORY_TIMESTAMP, INVALID_HISTORY_TIMESTAMP,
DOC_HISTORY_NOT_FOUND, DOC_HISTORY_NOT_FOUND,
BLOB_NOT_FOUND, BLOB_NOT_FOUND,
EXPECT_TO_PUBLISH_PAGE, EXPECT_TO_PUBLISH_DOC,
EXPECT_TO_REVOKE_PUBLIC_PAGE, EXPECT_TO_REVOKE_PUBLIC_DOC,
EXPECT_TO_GRANT_DOC_USER_ROLES, EXPECT_TO_GRANT_DOC_USER_ROLES,
EXPECT_TO_REVOKE_DOC_USER_ROLES, EXPECT_TO_REVOKE_DOC_USER_ROLES,
EXPECT_TO_UPDATE_DOC_USER_ROLE, EXPECT_TO_UPDATE_DOC_USER_ROLE,
PAGE_IS_NOT_PUBLIC, DOC_IS_NOT_PUBLIC,
FAILED_TO_SAVE_UPDATES, FAILED_TO_SAVE_UPDATES,
FAILED_TO_UPSERT_SNAPSHOT, FAILED_TO_UPSERT_SNAPSHOT,
ACTION_FORBIDDEN_ON_NON_TEAM_WORKSPACE, ACTION_FORBIDDEN_ON_NON_TEAM_WORKSPACE,
PAGE_DEFAULT_ROLE_CAN_NOT_BE_OWNER, DOC_DEFAULT_ROLE_CAN_NOT_BE_OWNER,
CAN_NOT_BATCH_GRANT_PAGE_OWNER_PERMISSIONS, CAN_NOT_BATCH_GRANT_DOC_OWNER_PERMISSIONS,
UNSUPPORTED_SUBSCRIPTION_PLAN, UNSUPPORTED_SUBSCRIPTION_PLAN,
FAILED_TO_CHECKOUT, FAILED_TO_CHECKOUT,
INVALID_CHECKOUT_PARAMETERS, INVALID_CHECKOUT_PARAMETERS,

View File

@@ -16,7 +16,7 @@ export {
fixupDocRole, fixupDocRole,
mapDocRoleToPermissions, mapDocRoleToPermissions,
mapWorkspaceRoleToPermissions, mapWorkspaceRoleToPermissions,
PublicPageMode, PublicDocMode,
WORKSPACE_ACTIONS, WORKSPACE_ACTIONS,
type WorkspaceActionPermissions, type WorkspaceActionPermissions,
WorkspaceRole, WorkspaceRole,

View File

@@ -1,10 +1,10 @@
import { Injectable, Logger } from '@nestjs/common'; 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 { PrismaClient, WorkspaceMemberStatus } from '@prisma/client';
import { groupBy } from 'lodash-es'; import { groupBy } from 'lodash-es';
import { import {
CanNotBatchGrantPageOwnerPermissions, CanNotBatchGrantDocOwnerPermissions,
DocAccessDenied, DocAccessDenied,
EventBus, EventBus,
OnEvent, OnEvent,
@@ -17,7 +17,7 @@ import {
docActionRequiredRole, docActionRequiredRole,
docActionRequiredWorkspaceRole, docActionRequiredWorkspaceRole,
DocRole, DocRole,
PublicPageMode, PublicDocMode,
WorkspaceRole, WorkspaceRole,
} from './types'; } from './types';
@@ -38,10 +38,10 @@ export class PermissionService {
return; return;
} }
await this.prisma.workspacePageUserPermission.createMany({ await this.prisma.workspaceDocUserPermission.createMany({
data: { data: {
workspaceId, workspaceId,
pageId: docId, docId,
userId: editor, userId: editor,
type: DocRole.Owner, type: DocRole.Owner,
createdAt: new Date(), createdAt: new Date(),
@@ -172,7 +172,7 @@ export class PermissionService {
// if workspace is public or have any public page, then allow to access // if workspace is public or have any public page, then allow to access
const [isPublicWorkspace, publicPages] = await Promise.all([ const [isPublicWorkspace, publicPages] = await Promise.all([
this.tryCheckWorkspace(ws, user, WorkspaceRole.Collaborator), this.tryCheckWorkspace(ws, user, WorkspaceRole.Collaborator),
this.prisma.workspacePage.count({ this.prisma.workspaceDoc.count({
where: { where: {
workspaceId: ws, workspaceId: ws,
public: true, public: true,
@@ -555,17 +555,17 @@ export class PermissionService {
async tryCheckPage( async tryCheckPage(
ws: string, ws: string,
page: string, doc: string,
action: DocAction, action: DocAction,
user?: string user?: string
) { ) {
const role = docActionRequiredRole(action); const role = docActionRequiredRole(action);
// check whether page is public // check whether page is public
if (action === 'Doc.Read') { if (action === 'Doc.Read') {
const count = await this.prisma.workspacePage.count({ const count = await this.prisma.workspaceDoc.count({
where: { where: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
public: true, public: true,
}, },
}); });
@@ -579,20 +579,20 @@ export class PermissionService {
if (user) { if (user) {
const [roleEntity, pageEntity, workspaceRoleEntity] = await Promise.all([ const [roleEntity, pageEntity, workspaceRoleEntity] = await Promise.all([
this.prisma.workspacePageUserPermission.findFirst({ this.prisma.workspaceDocUserPermission.findFirst({
where: { where: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
userId: user, userId: user,
}, },
select: { select: {
type: true, type: true,
}, },
}), }),
this.prisma.workspacePage.findFirst({ this.prisma.workspaceDoc.findFirst({
where: { where: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
}, },
select: { select: {
defaultRole: true, defaultRole: true,
@@ -629,7 +629,7 @@ export class PermissionService {
} }
this.logger.log("User's role is lower than required", { this.logger.log("User's role is lower than required", {
workspaceId: ws, workspaceId: ws,
docId: page, docId: doc,
userId: user, userId: user,
workspaceRole: workspaceRoleEntity workspaceRole: workspaceRoleEntity
? WorkspaceRole[workspaceRoleEntity.type] ? WorkspaceRole[workspaceRoleEntity.type]
@@ -651,24 +651,24 @@ export class PermissionService {
); );
} }
async isPublicPage(ws: string, page: string) { async isPublicPage(ws: string, doc: string) {
return this.prisma.workspacePage return this.prisma.workspaceDoc
.count({ .count({
where: { where: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
public: true, public: true,
}, },
}) })
.then(count => count > 0); .then(count => count > 0);
} }
async publishPage(ws: string, page: string, mode = PublicPageMode.Page) { async publishPage(ws: string, doc: string, mode = PublicDocMode.Page) {
return this.prisma.workspacePage.upsert({ return this.prisma.workspaceDoc.upsert({
where: { where: {
workspaceId_pageId: { workspaceId_docId: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
}, },
}, },
update: { update: {
@@ -677,19 +677,19 @@ export class PermissionService {
}, },
create: { create: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
mode, mode,
public: true, public: true,
}, },
}); });
} }
async revokePublicPage(ws: string, page: string) { async revokePublicPage(ws: string, doc: string) {
return this.prisma.workspacePage.upsert({ return this.prisma.workspaceDoc.upsert({
where: { where: {
workspaceId_pageId: { workspaceId_docId: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
}, },
}, },
update: { update: {
@@ -697,20 +697,20 @@ export class PermissionService {
}, },
create: { create: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
public: false, 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( const [p] = await this.prisma.$transaction(
[ [
this.prisma.workspacePageUserPermission.upsert({ this.prisma.workspaceDocUserPermission.upsert({
where: { where: {
workspaceId_pageId_userId: { workspaceId_docId_userId: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
userId: user, userId: user,
}, },
}, },
@@ -719,7 +719,7 @@ export class PermissionService {
}, },
create: { create: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
userId: user, userId: user,
type: permission, type: permission,
}, },
@@ -727,10 +727,10 @@ export class PermissionService {
// If the new permission is owner, we need to revoke old owner // If the new permission is owner, we need to revoke old owner
permission === DocRole.Owner permission === DocRole.Owner
? this.prisma.workspacePageUserPermission.updateMany({ ? this.prisma.workspaceDocUserPermission.updateMany({
where: { where: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
type: DocRole.Owner, type: DocRole.Owner,
userId: { userId: {
not: user, not: user,
@@ -744,14 +744,14 @@ export class PermissionService {
].filter(Boolean) as Prisma.PrismaPromise<any>[] ].filter(Boolean) as Prisma.PrismaPromise<any>[]
); );
return p as WorkspacePageUserPermission; return p as WorkspaceDocUserPermission;
} }
async revokePage(ws: string, page: string, user: string) { async revokePage(ws: string, doc: string, user: string) {
const result = await this.prisma.workspacePageUserPermission.deleteMany({ const result = await this.prisma.workspaceDocUserPermission.deleteMany({
where: { where: {
workspaceId: ws, workspaceId: ws,
pageId: page, docId: doc,
userId: user, userId: user,
type: { type: {
// We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading // We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading
@@ -765,7 +765,7 @@ export class PermissionService {
async batchGrantPage( async batchGrantPage(
workspaceId: string, workspaceId: string,
pageId: string, docId: string,
userIds: string[], userIds: string[],
role: DocRole role: DocRole
) { ) {
@@ -774,14 +774,14 @@ export class PermissionService {
} }
if (role === DocRole.Owner) { 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, skipDuplicates: true,
data: userIds.map(id => ({ data: userIds.map(id => ({
workspaceId, workspaceId,
pageId, docId,
userId: id, userId: id,
type: role, type: role,
})), })),

View File

@@ -1,6 +1,6 @@
import { LeafPaths, LeafVisitor } from '../../base'; import { LeafPaths, LeafVisitor } from '../../base';
export enum PublicPageMode { export enum PublicDocMode {
Page, Page,
Edgeless, Edgeless,
} }

View File

@@ -13,7 +13,7 @@ import {
} from '../../base'; } from '../../base';
import { CurrentUser, Public } from '../auth'; import { CurrentUser, Public } from '../auth';
import { PgWorkspaceDocStorageAdapter } from '../doc'; import { PgWorkspaceDocStorageAdapter } from '../doc';
import { PermissionService, PublicPageMode } from '../permission'; import { PermissionService, PublicDocMode } from '../permission';
import { WorkspaceBlobStorage } from '../storage'; import { WorkspaceBlobStorage } from '../storage';
import { DocID } from '../utils/doc'; import { DocID } from '../utils/doc';
@@ -109,16 +109,16 @@ export class WorkspacesController {
if (!docId.isWorkspace) { if (!docId.isWorkspace) {
// fetch the publish page mode for publish page // fetch the publish page mode for publish page
const publishPage = await this.prisma.workspacePage.findUnique({ const publishPage = await this.prisma.workspaceDoc.findUnique({
where: { where: {
workspaceId_pageId: { workspaceId_docId: {
workspaceId: docId.workspace, workspaceId: docId.workspace,
pageId: docId.guid, docId: docId.guid,
}, },
}, },
}); });
const publishPageMode = const publishPageMode =
publishPage?.mode === PublicPageMode.Edgeless ? 'edgeless' : 'page'; publishPage?.mode === PublicDocMode.Edgeless ? 'edgeless' : 'page';
res.setHeader('publish-mode', publishPageMode); res.setHeader('publish-mode', publishPageMode);
} }

View File

@@ -10,9 +10,10 @@ import { UserModule } from '../user';
import { WorkspacesController } from './controller'; import { WorkspacesController } from './controller';
import { import {
DocHistoryResolver, DocHistoryResolver,
PagePermissionResolver, DocResolver,
TeamWorkspaceResolver, TeamWorkspaceResolver,
WorkspaceBlobResolver, WorkspaceBlobResolver,
WorkspaceDocResolver,
WorkspaceResolver, WorkspaceResolver,
WorkspaceService, WorkspaceService,
} from './resolvers'; } from './resolvers';
@@ -31,7 +32,8 @@ import {
providers: [ providers: [
WorkspaceResolver, WorkspaceResolver,
TeamWorkspaceResolver, TeamWorkspaceResolver,
PagePermissionResolver, WorkspaceDocResolver,
DocResolver,
DocHistoryResolver, DocHistoryResolver,
WorkspaceBlobResolver, WorkspaceBlobResolver,
WorkspaceService, WorkspaceService,

View File

@@ -10,18 +10,18 @@ import {
ResolveField, ResolveField,
Resolver, Resolver,
} from '@nestjs/graphql'; } 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 { PrismaClient } from '@prisma/client';
import { import {
DocAccessDenied, DocAccessDenied,
DocDefaultRoleCanNotBeOwner,
DocIsNotPublic,
ExpectToGrantDocUserRoles, ExpectToGrantDocUserRoles,
ExpectToPublishPage, ExpectToPublishDoc,
ExpectToRevokeDocUserRoles, ExpectToRevokeDocUserRoles,
ExpectToRevokePublicPage, ExpectToRevokePublicDoc,
ExpectToUpdateDocUserRole, ExpectToUpdateDocUserRole,
PageDefaultRoleCanNotBeOwner,
PageIsNotPublic,
paginate, paginate,
Paginated, Paginated,
PaginationInput, PaginationInput,
@@ -36,27 +36,27 @@ import {
fixupDocRole, fixupDocRole,
mapDocRoleToPermissions, mapDocRoleToPermissions,
PermissionService, PermissionService,
PublicPageMode, PublicDocMode,
} from '../../permission'; } from '../../permission';
import { PublicUserType } from '../../user'; import { PublicUserType } from '../../user';
import { DocID } from '../../utils/doc'; import { DocID } from '../../utils/doc';
import { WorkspaceType } from '../types'; import { WorkspaceType } from '../types';
registerEnumType(PublicPageMode, { registerEnumType(PublicDocMode, {
name: 'PublicPageMode', name: 'PublicDocMode',
description: 'The mode which the public page default in', description: 'The mode which the public doc default in',
}); });
@ObjectType() @ObjectType()
class WorkspacePage implements Partial<PrismaWorkspacePage> { class DocType implements Partial<PrismaWorkspaceDoc> {
@Field(() => String, { name: 'id' }) @Field(() => String, { name: 'id' })
pageId!: string; docId!: string;
@Field() @Field()
workspaceId!: string; workspaceId!: string;
@Field(() => PublicPageMode) @Field(() => PublicDocMode)
mode!: PublicPageMode; mode!: PublicDocMode;
@Field() @Field()
public!: boolean; public!: boolean;
@@ -105,7 +105,7 @@ class RevokeDocUserRoleInput {
} }
@InputType() @InputType()
class UpdatePageDefaultRoleInput { class UpdateDocDefaultRoleInput {
@Field(() => String) @Field(() => String)
docId!: string; docId!: string;
@@ -144,13 +144,7 @@ const DocPermissions = registerObjectType<DocActionPermissions>(
); );
@ObjectType() @ObjectType()
class DocType { export class DocRolePermissions {
@Field(() => String)
id!: string;
@Field(() => Boolean)
public!: boolean;
@Field(() => DocRole) @Field(() => DocRole)
role!: DocRole; role!: DocRole;
@@ -159,8 +153,207 @@ class DocType {
} }
@Resolver(() => WorkspaceType) @Resolver(() => WorkspaceType)
export class PagePermissionResolver { export class WorkspaceDocResolver {
private readonly logger = new Logger(PagePermissionResolver.name); 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( constructor(
private readonly prisma: PrismaClient, private readonly prisma: PrismaClient,
@@ -168,96 +361,31 @@ export class PagePermissionResolver {
private readonly models: Models private readonly models: Models
) {} ) {}
/** @ResolveField(() => DocPermissions)
* @deprecated async permissions(
*/ @CurrentUser() user: CurrentUser,
@ResolveField(() => [String], { @Parent() doc: DocType
description: 'Shared pages of workspace', ): Promise<DocRolePermissions> {
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,
},
});
const [permission, workspacePermission] = await this.prisma.$transaction( const [permission, workspacePermission] = await this.prisma.$transaction(
tx => tx =>
Promise.all([ Promise.all([
tx.workspacePageUserPermission.findFirst({ tx.workspaceDocUserPermission.findFirst({
where: { where: {
workspaceId: workspace.id, workspaceId: doc.workspaceId,
pageId, docId: doc.docId,
userId: user.id, userId: user.id,
}, },
}), }),
tx.workspaceUserPermission.findFirst({ tx.workspaceUserPermission.findFirst({
where: { where: {
workspaceId: workspace.id, workspaceId: doc.workspaceId,
userId: user.id, userId: user.id,
}, },
}), }),
]) ])
); );
return { return {
id: pageId,
public: page?.public ?? false,
role: permission?.type ?? DocRole.External, role: permission?.type ?? DocRole.External,
permissions: mapDocRoleToPermissions( permissions: mapDocRoleToPermissions(
fixupDocRole(workspacePermission?.type, permission?.type) fixupDocRole(workspacePermission?.type, permission?.type)
@@ -266,29 +394,27 @@ export class PagePermissionResolver {
} }
@ResolveField(() => PaginatedGrantedDocUserType, { @ResolveField(() => PaginatedGrantedDocUserType, {
description: 'Page granted users list', description: 'paginated doc granted users list',
complexity: 4, complexity: 4,
}) })
async pageGrantedUsersList( async grantedUsersList(
@CurrentUser() user: CurrentUser, @CurrentUser() user: CurrentUser,
@Parent() workspace: WorkspaceType, @Parent() doc: DocType,
@Args('pageId') pageId: string,
@Args('pagination') pagination: PaginationInput @Args('pagination') pagination: PaginationInput
): Promise<PaginatedGrantedDocUserType> { ): Promise<PaginatedGrantedDocUserType> {
await this.permission.checkPagePermission( await this.permission.checkPagePermission(
workspace.id, doc.workspaceId,
pageId, doc.docId,
'Doc.Users.Read', 'Doc.Users.Read',
user.id user.id
); );
const docId = new DocID(pageId, workspace.id);
const [permissions, totalCount] = await this.prisma.$transaction(tx => { const [permissions, totalCount] = await this.prisma.$transaction(tx => {
return Promise.all([ return Promise.all([
tx.workspacePageUserPermission.findMany({ tx.workspaceDocUserPermission.findMany({
where: { where: {
workspaceId: workspace.id, workspaceId: doc.workspaceId,
pageId: docId.guid, docId: doc.docId,
createdAt: pagination.after createdAt: pagination.after
? { ? {
gt: pagination.after, gt: pagination.after,
@@ -306,10 +432,10 @@ export class PagePermissionResolver {
take: pagination.first, take: pagination.first,
skip: pagination.offset, skip: pagination.offset,
}), }),
tx.workspacePageUserPermission.count({ tx.workspaceDocUserPermission.count({
where: { where: {
workspaceId: workspace.id, workspaceId: doc.workspaceId,
pageId: docId.guid, 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) @Mutation(() => Boolean)
async grantDocUserRoles( async grantDocUserRoles(
@CurrentUser() user: CurrentUser, @CurrentUser() user: CurrentUser,
@@ -580,13 +591,13 @@ export class PagePermissionResolver {
} }
@Mutation(() => Boolean) @Mutation(() => Boolean)
async updatePageDefaultRole( async updateDocDefaultRole(
@CurrentUser() user: CurrentUser, @CurrentUser() user: CurrentUser,
@Args('input') input: UpdatePageDefaultRoleInput @Args('input') input: UpdateDocDefaultRoleInput
) { ) {
if (input.role === DocRole.Owner) { if (input.role === DocRole.Owner) {
this.logger.log('Page default role can not be owner', input); this.logger.log('Doc default role can not be owner', input);
throw new PageDefaultRoleCanNotBeOwner(); throw new DocDefaultRoleCanNotBeOwner();
} }
const doc = new DocID(input.docId, input.workspaceId); const doc = new DocID(input.docId, input.workspaceId);
const pairs = { const pairs = {
@@ -622,11 +633,11 @@ export class PagePermissionResolver {
} }
throw error; throw error;
} }
await this.prisma.workspacePage.upsert({ await this.prisma.workspaceDoc.upsert({
where: { where: {
workspaceId_pageId: { workspaceId_docId: {
workspaceId: doc.workspace, workspaceId: doc.workspace,
pageId: doc.guid, docId: doc.guid,
}, },
}, },
update: { update: {
@@ -634,7 +645,7 @@ export class PagePermissionResolver {
}, },
create: { create: {
workspaceId: doc.workspace, workspaceId: doc.workspace,
pageId: doc.guid, docId: doc.guid,
defaultRole: input.role, defaultRole: input.role,
}, },
}); });

View File

@@ -1,6 +1,6 @@
export * from './blob'; export * from './blob';
export * from './doc';
export * from './history'; export * from './history';
export * from './page';
export * from './service'; export * from './service';
export * from './team'; export * from './team';
export * from './workspace'; export * from './workspace';

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Transactional } from '@nestjs-cls/transactional'; import { Transactional } from '@nestjs-cls/transactional';
import { import {
type WorkspacePage as Page, type WorkspaceDoc as Page,
type WorkspacePageUserPermission as PageUserPermission, type WorkspaceDocUserPermission as PageUserPermission,
} from '@prisma/client'; } from '@prisma/client';
import { WorkspaceRole } from '../core/permission'; import { WorkspaceRole } from '../core/permission';
@@ -21,12 +21,12 @@ export class PageModel extends BaseModel {
/** /**
* Create or update the page. * Create or update the page.
*/ */
async upsert(workspaceId: string, pageId: string, data?: UpdatePageInput) { async upsert(workspaceId: string, docId: string, data?: UpdatePageInput) {
return await this.db.workspacePage.upsert({ return await this.db.workspaceDoc.upsert({
where: { where: {
workspaceId_pageId: { workspaceId_docId: {
workspaceId, workspaceId,
pageId, docId,
}, },
}, },
update: { update: {
@@ -35,7 +35,7 @@ export class PageModel extends BaseModel {
create: { create: {
...data, ...data,
workspaceId, 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. * @param isPublic: if true, only return the public page. If false, only return the private page.
* If not set, return public or private both. * If not set, return public or private both.
*/ */
async get(workspaceId: string, pageId: string, isPublic?: boolean) { async get(workspaceId: string, docId: string, isPublic?: boolean) {
return await this.db.workspacePage.findUnique({ return await this.db.workspaceDoc.findUnique({
where: { where: {
workspaceId_pageId: { workspaceId_docId: {
workspaceId, workspaceId,
pageId, docId,
}, },
public: isPublic, public: isPublic,
}, },
@@ -61,7 +61,7 @@ export class PageModel extends BaseModel {
* Find the workspace public pages. * Find the workspace public pages.
*/ */
async findPublics(workspaceId: string) { async findPublics(workspaceId: string) {
return await this.db.workspacePage.findMany({ return await this.db.workspaceDoc.findMany({
where: { where: {
workspaceId, workspaceId,
public: true, public: true,
@@ -73,7 +73,7 @@ export class PageModel extends BaseModel {
* Get the workspace public pages count. * Get the workspace public pages count.
*/ */
async getPublicsCount(workspaceId: string) { async getPublicsCount(workspaceId: string) {
return await this.db.workspacePage.count({ return await this.db.workspaceDoc.count({
where: { where: {
workspaceId, workspaceId,
public: true, public: true,
@@ -91,15 +91,15 @@ export class PageModel extends BaseModel {
@Transactional() @Transactional()
async grantMember( async grantMember(
workspaceId: string, workspaceId: string,
pageId: string, docId: string,
userId: string, userId: string,
permission: WorkspaceRole = WorkspaceRole.Collaborator permission: WorkspaceRole = WorkspaceRole.Collaborator
): Promise<PageUserPermission> { ): Promise<PageUserPermission> {
let data = await this.db.workspacePageUserPermission.findUnique({ let data = await this.db.workspaceDocUserPermission.findUnique({
where: { where: {
workspaceId_pageId_userId: { workspaceId_docId_userId: {
workspaceId, workspaceId,
pageId, docId,
userId, userId,
}, },
}, },
@@ -109,11 +109,11 @@ export class PageModel extends BaseModel {
if (!data || data.type !== permission) { if (!data || data.type !== permission) {
if (data) { if (data) {
// Update the permission // Update the permission
data = await this.db.workspacePageUserPermission.update({ data = await this.db.workspaceDocUserPermission.update({
where: { where: {
workspaceId_pageId_userId: { workspaceId_docId_userId: {
workspaceId, workspaceId,
pageId, docId,
userId, userId,
}, },
}, },
@@ -121,10 +121,10 @@ export class PageModel extends BaseModel {
}); });
} else { } else {
// Create a new permission // Create a new permission
data = await this.db.workspacePageUserPermission.create({ data = await this.db.workspaceDocUserPermission.create({
data: { data: {
workspaceId, workspaceId,
pageId, docId,
userId, userId,
type: permission, type: permission,
}, },
@@ -133,17 +133,17 @@ export class PageModel extends BaseModel {
// If the new permission is owner, we need to revoke old owner // If the new permission is owner, we need to revoke old owner
if (permission === WorkspaceRole.Owner) { if (permission === WorkspaceRole.Owner) {
await this.db.workspacePageUserPermission.updateMany({ await this.db.workspaceDocUserPermission.updateMany({
where: { where: {
workspaceId, workspaceId,
pageId, docId,
type: WorkspaceRole.Owner, type: WorkspaceRole.Owner,
userId: { not: userId }, userId: { not: userId },
}, },
data: { type: WorkspaceRole.Admin }, data: { type: WorkspaceRole.Admin },
}); });
this.logger.log( 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; return data;
@@ -159,14 +159,14 @@ export class PageModel extends BaseModel {
*/ */
async isMember( async isMember(
workspaceId: string, workspaceId: string,
pageId: string, docId: string,
userId: string, userId: string,
permission: WorkspaceRole = WorkspaceRole.Collaborator permission: WorkspaceRole = WorkspaceRole.Collaborator
) { ) {
const count = await this.db.workspacePageUserPermission.count({ const count = await this.db.workspaceDocUserPermission.count({
where: { where: {
workspaceId, workspaceId,
pageId, docId,
userId, userId,
type: { type: {
gte: permission, gte: permission,
@@ -180,11 +180,11 @@ export class PageModel extends BaseModel {
* Delete a page member * Delete a page member
* Except the owner, the owner can't be deleted. * Except the owner, the owner can't be deleted.
*/ */
async deleteMember(workspaceId: string, pageId: string, userId: string) { async deleteMember(workspaceId: string, docId: string, userId: string) {
const { count } = await this.db.workspacePageUserPermission.deleteMany({ const { count } = await this.db.workspaceDocUserPermission.deleteMany({
where: { where: {
workspaceId, workspaceId,
pageId, docId,
userId, userId,
type: { type: {
// We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading // We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading

View File

@@ -230,10 +230,13 @@ enum DocRole {
} }
type DocType { type DocType {
"""paginated doc granted users list"""
grantedUsersList(pagination: PaginationInput!): PaginatedGrantedDocUserType!
id: String! id: String!
mode: PublicDocMode!
permissions: DocPermissions! permissions: DocPermissions!
public: Boolean! public: Boolean!
role: DocRole! workspaceId: String!
} }
type EditorType { type EditorType {
@@ -254,7 +257,7 @@ enum ErrorNames {
CANNOT_DELETE_ALL_ADMIN_ACCOUNT CANNOT_DELETE_ALL_ADMIN_ACCOUNT
CANNOT_DELETE_OWN_ACCOUNT CANNOT_DELETE_OWN_ACCOUNT
CANT_UPDATE_ONETIME_PAYMENT_SUBSCRIPTION CANT_UPDATE_ONETIME_PAYMENT_SUBSCRIPTION
CAN_NOT_BATCH_GRANT_PAGE_OWNER_PERMISSIONS CAN_NOT_BATCH_GRANT_DOC_OWNER_PERMISSIONS
CAPTCHA_VERIFICATION_FAILED CAPTCHA_VERIFICATION_FAILED
COPILOT_ACTION_TAKEN COPILOT_ACTION_TAKEN
COPILOT_FAILED_TO_CREATE_MESSAGE COPILOT_FAILED_TO_CREATE_MESSAGE
@@ -268,16 +271,18 @@ 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_HISTORY_NOT_FOUND DOC_HISTORY_NOT_FOUND
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_DOC
EXPECT_TO_REVOKE_DOC_USER_ROLES EXPECT_TO_REVOKE_DOC_USER_ROLES
EXPECT_TO_REVOKE_PUBLIC_PAGE 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
@@ -306,8 +311,6 @@ 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_DEFAULT_ROLE_CAN_NOT_BE_OWNER
PAGE_IS_NOT_PUBLIC
PASSWORD_REQUIRED PASSWORD_REQUIRED
QUERY_TOO_LONG QUERY_TOO_LONG
RUNTIME_CONFIG_NOT_FOUND RUNTIME_CONFIG_NOT_FOUND
@@ -621,7 +624,8 @@ type Mutation {
invite(email: String!, permission: Permission @deprecated(reason: "never used"), sendInviteMail: Boolean, workspaceId: String!): String! invite(email: String!, permission: Permission @deprecated(reason: "never used"), sendInviteMail: Boolean, workspaceId: String!): String!
inviteBatch(emails: [String!]!, sendInviteMail: Boolean, workspaceId: String!): [InviteResult!]! inviteBatch(emails: [String!]!, sendInviteMail: Boolean, workspaceId: String!): [InviteResult!]!
leaveWorkspace(sendLeaveMail: Boolean, workspaceId: String!, workspaceName: String @deprecated(reason: "no longer used")): Boolean! 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! recoverDoc(guid: String!, timestamp: DateTime!, workspaceId: String!): DateTime!
releaseDeletedBlobs(workspaceId: String!): Boolean! releaseDeletedBlobs(workspaceId: String!): Boolean!
@@ -632,23 +636,22 @@ type Mutation {
revoke(userId: String!, workspaceId: String!): Boolean! revoke(userId: String!, workspaceId: String!): Boolean!
revokeDocUserRoles(input: RevokeDocUserRoleInput!): Boolean! revokeDocUserRoles(input: RevokeDocUserRoleInput!): Boolean!
revokeInviteLink(workspaceId: String!): Boolean! revokeInviteLink(workspaceId: String!): Boolean!
revokePage(pageId: String!, workspaceId: String!): Boolean! @deprecated(reason: "use revokePublicPage") revokePublicDoc(docId: String!, workspaceId: String!): DocType!
revokePublicPage(pageId: String!, workspaceId: String!): WorkspacePage! revokePublicPage(docId: String!, workspaceId: String!): DocType! @deprecated(reason: "use revokePublicDoc instead")
sendChangeEmail(callbackUrl: String!, email: String): Boolean! sendChangeEmail(callbackUrl: String!, email: String): Boolean!
sendChangePasswordEmail(callbackUrl: String!, email: String @deprecated(reason: "fetched from signed in user")): 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! sendSetPasswordEmail(callbackUrl: String!, email: String @deprecated(reason: "fetched from signed in user")): Boolean!
sendVerifyChangeEmail(callbackUrl: String!, email: String!, token: String!): Boolean! sendVerifyChangeEmail(callbackUrl: String!, email: String!, token: String!): Boolean!
sendVerifyEmail(callbackUrl: String!): Boolean! sendVerifyEmail(callbackUrl: String!): Boolean!
setBlob(blob: Upload!, workspaceId: String!): String! setBlob(blob: Upload!, workspaceId: String!): String!
sharePage(pageId: String!, workspaceId: String!): Boolean! @deprecated(reason: "renamed to publishPage")
"""Update a copilot prompt""" """Update a copilot prompt"""
updateCopilotPrompt(messages: [CopilotPromptMessageInput!]!, name: String!): CopilotPromptType! updateCopilotPrompt(messages: [CopilotPromptMessageInput!]!, name: String!): CopilotPromptType!
"""Update a chat session""" """Update a chat session"""
updateCopilotSession(options: UpdateChatSessionInput!): String! updateCopilotSession(options: UpdateChatSessionInput!): String!
updateDocDefaultRole(input: UpdateDocDefaultRoleInput!): Boolean!
updateDocUserRole(input: UpdateDocUserRoleInput!): Boolean! updateDocUserRole(input: UpdateDocUserRoleInput!): Boolean!
updatePageDefaultRole(input: UpdatePageDefaultRoleInput!): Boolean!
updateProfile(input: UpdateUserInput!): UserType! updateProfile(input: UpdateUserInput!): UserType!
"""update server runtime configurable setting""" """update server runtime configurable setting"""
@@ -719,8 +722,8 @@ enum Permission {
Owner Owner
} }
"""The mode which the public page default in""" """The mode which the public doc default in"""
enum PublicPageMode { enum PublicDocMode {
Edgeless Edgeless
Page Page
} }
@@ -1005,6 +1008,12 @@ input UpdateChatSessionInput {
sessionId: String! sessionId: String!
} }
input UpdateDocDefaultRoleInput {
docId: String!
role: DocRole!
workspaceId: String!
}
input UpdateDocUserRoleInput { input UpdateDocUserRoleInput {
docId: String! docId: String!
role: DocRole! role: DocRole!
@@ -1012,12 +1021,6 @@ input UpdateDocUserRoleInput {
workspaceId: String! workspaceId: String!
} }
input UpdatePageDefaultRoleInput {
docId: String!
role: DocRole!
workspaceId: String!
}
input UpdateUserInput { input UpdateUserInput {
"""User name""" """User name"""
name: String name: String
@@ -1128,13 +1131,6 @@ type WorkspaceMembersExceedLimitToDowngradeDataType {
limit: Int! limit: Int!
} }
type WorkspacePage {
id: String!
mode: PublicPageMode!
public: Boolean!
workspaceId: String!
}
type WorkspacePageMeta { type WorkspacePageMeta {
createdAt: DateTime! createdAt: DateTime!
createdBy: EditorType createdBy: EditorType
@@ -1199,6 +1195,9 @@ type WorkspaceType {
"""Workspace created date""" """Workspace created date"""
createdAt: DateTime! createdAt: DateTime!
"""Get get with given id"""
doc(docId: String!): DocType!
"""Enable AI""" """Enable AI"""
enableAi: Boolean! enableAi: Boolean!
@@ -1229,23 +1228,21 @@ type WorkspaceType {
"""Owner of workspace""" """Owner of workspace"""
owner: UserType! owner: UserType!
"""Page granted users list"""
pageGrantedUsersList(pageId: String!, pagination: PaginationInput!): PaginatedGrantedDocUserType!
"""Cloud page metadata of workspace""" """Cloud page metadata of workspace"""
pageMeta(pageId: String!): WorkspacePageMeta! pageMeta(pageId: String!): WorkspacePageMeta!
"""Check if current user has permission to access the page"""
pagePermission(pageId: String!): DocType!
"""is Public workspace""" """is Public workspace"""
public: Boolean! public: Boolean!
"""Get public page of a workspace by page id.""" """Get public page of a workspace by page id."""
publicPage(pageId: String!): WorkspacePage publicDoc(docId: String!): DocType
"""Public pages of a workspace""" """Get public docs of a workspace"""
publicPages: [WorkspacePage!]! 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 of workspace"""
quota: WorkspaceQuotaType! quota: WorkspaceQuotaType!
@@ -1253,9 +1250,6 @@ type WorkspaceType {
"""Role of current signed in user in workspace""" """Role of current signed in user in workspace"""
role: Permission! role: Permission!
"""Shared pages of workspace"""
sharedPages: [String!]! @deprecated(reason: "use WorkspaceType.publicPages")
"""The team subscription of the workspace, if exists.""" """The team subscription of the workspace, if exists."""
subscription: SubscriptionType subscription: SubscriptionType

View File

@@ -6,7 +6,7 @@ import { ServerService } from '@affine/core/modules/cloud';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { WorkspacePermissionService } from '@affine/core/modules/permissions'; import { WorkspacePermissionService } from '@affine/core/modules/permissions';
import { ShareInfoService } from '@affine/core/modules/share-doc'; import { ShareInfoService } from '@affine/core/modules/share-doc';
import { PublicPageMode } from '@affine/graphql'; import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import { import {
@@ -92,7 +92,7 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
} }
try { try {
// TODO(@JimmFly): remove mode when we have a better way to handle it // 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(); track.$.sharePanel.$.createShareLink();
notify.success({ notify.success({
title: title:

View File

@@ -4,7 +4,7 @@ import { FavoriteTag } from '@affine/core/components/page-list/components/favori
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import { WorkspaceService } from '@affine/core/modules/workspace'; import { WorkspaceService } from '@affine/core/modules/workspace';
import { PublicPageMode } from '@affine/graphql'; import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import type { DocMeta, Workspace } from '@blocksuite/affine/store'; import type { DocMeta, Workspace } from '@blocksuite/affine/store';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
@@ -68,9 +68,9 @@ export const useAllPageListConfig = () => {
allPages: pageMetas, allPages: pageMetas,
getPublicMode(id) { getPublicMode(id) {
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode; const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
if (mode === PublicPageMode.Edgeless) { if (mode === PublicDocMode.Edgeless) {
return 'edgeless'; return 'edgeless';
} else if (mode === PublicPageMode.Page) { } else if (mode === PublicDocMode.Page) {
return 'page'; return 'page';
} else { } else {
return undefined; return undefined;

View File

@@ -3,7 +3,7 @@ import { useBlockSuiteDocMeta } from '@affine/core/components/hooks/use-block-su
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import { WorkspaceService } from '@affine/core/modules/workspace'; import { WorkspaceService } from '@affine/core/modules/workspace';
import { PublicPageMode } from '@affine/graphql'; import { PublicDocMode } from '@affine/graphql';
import { Trans, useI18n } from '@affine/i18n'; import { Trans, useI18n } from '@affine/i18n';
import type { DocMeta } from '@blocksuite/affine/store'; import type { DocMeta } from '@blocksuite/affine/store';
import { FilterIcon } from '@blocksuite/icons/rc'; import { FilterIcon } from '@blocksuite/icons/rc';
@@ -76,9 +76,9 @@ export const SelectPage = ({
const getPublicMode = useCallback( const getPublicMode = useCallback(
(id: string) => { (id: string) => {
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode; const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
if (mode === PublicPageMode.Edgeless) { if (mode === PublicDocMode.Edgeless) {
return 'edgeless'; return 'edgeless';
} else if (mode === PublicPageMode.Page) { } else if (mode === PublicDocMode.Page) {
return 'page'; return 'page';
} else { } else {
return undefined; return undefined;

View File

@@ -1,7 +1,7 @@
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { Collection, Filter } from '@affine/env/filter'; 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 type { DocMeta } from '@blocksuite/affine/store';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useEffect, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
@@ -23,7 +23,7 @@ export const useFilteredPageMetas = (
(id: string) => { (id: string) => {
const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode; const mode = shareDocs?.find(shareDoc => shareDoc.id === id)?.mode;
return mode return mode
? mode === PublicPageMode.Edgeless ? mode === PublicDocMode.Edgeless
? ('edgeless' as const) ? ('edgeless' as const)
: ('page' as const) : ('page' as const)
: undefined; : undefined;

View File

@@ -8,7 +8,7 @@ import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { GlobalContextService } from '@affine/core/modules/global-context'; import { GlobalContextService } from '@affine/core/modules/global-context';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { Collection } from '@affine/env/filter'; import type { Collection } from '@affine/env/filter';
import { PublicPageMode } from '@affine/graphql'; import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import track from '@affine/track'; import track from '@affine/track';
import type { DocMeta } from '@blocksuite/affine/store'; import type { DocMeta } from '@blocksuite/affine/store';
@@ -161,9 +161,9 @@ const ExplorerCollectionNodeChildren = ({
const pageData = { const pageData = {
meta: meta as DocMeta, meta: meta as DocMeta,
publicMode: publicMode:
publicMode === PublicPageMode.Edgeless publicMode === PublicDocMode.Edgeless
? ('edgeless' as const) ? ('edgeless' as const)
: publicMode === PublicPageMode.Page : publicMode === PublicDocMode.Page
? ('page' as const) ? ('page' as const)
: undefined, : undefined,
favorite: favourites.some(fav => fav.id === meta.id), favorite: favourites.some(fav => fav.id === meta.id),

View File

@@ -14,7 +14,7 @@ import { GlobalContextService } from '@affine/core/modules/global-context';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { AffineDNDData } from '@affine/core/types/dnd'; import type { AffineDNDData } from '@affine/core/types/dnd';
import type { Collection } from '@affine/env/filter'; import type { Collection } from '@affine/env/filter';
import { PublicPageMode } from '@affine/graphql'; import { PublicDocMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import type { DocMeta } from '@blocksuite/affine/store'; import type { DocMeta } from '@blocksuite/affine/store';
@@ -283,9 +283,9 @@ const ExplorerCollectionNodeChildren = ({
const pageData = { const pageData = {
meta: meta as DocMeta, meta: meta as DocMeta,
publicMode: publicMode:
publicMode === PublicPageMode.Edgeless publicMode === PublicDocMode.Edgeless
? ('edgeless' as const) ? ('edgeless' as const)
: publicMode === PublicPageMode.Page : publicMode === PublicDocMode.Page
? ('page' as const) ? ('page' as const)
: undefined, : undefined,
favorite: favourites.some(fav => fav.id === meta.id), favorite: favourites.some(fav => fav.id === meta.id),

View File

@@ -18,8 +18,7 @@ import type { GlobalCache } from '../../storage';
import type { WorkspaceService } from '../../workspace'; import type { WorkspaceService } from '../../workspace';
import type { ShareDocsStore } from '../stores/share-docs'; import type { ShareDocsStore } from '../stores/share-docs';
type ShareDocListType = type ShareDocListType = GetWorkspacePublicPagesQuery['workspace']['publicDocs'];
GetWorkspacePublicPagesQuery['workspace']['publicPages'];
export const logger = new DebugLogger('affine:share-doc-list'); export const logger = new DebugLogger('affine:share-doc-list');

View File

@@ -1,6 +1,6 @@
import type { import type {
GetWorkspacePublicPageByIdQuery, GetWorkspacePublicPageByIdQuery,
PublicPageMode, PublicDocMode,
} from '@affine/graphql'; } from '@affine/graphql';
import { import {
backoffRetry, backoffRetry,
@@ -20,7 +20,7 @@ import type { DocService } from '../../doc';
import type { WorkspaceService } from '../../workspace'; import type { WorkspaceService } from '../../workspace';
import type { ShareStore } from '../stores/share'; import type { ShareStore } from '../stores/share';
type ShareInfoType = GetWorkspacePublicPageByIdQuery['workspace']['publicPage']; type ShareInfoType = GetWorkspacePublicPageByIdQuery['workspace']['publicDoc'];
export class ShareInfo extends Entity { export class ShareInfo extends Entity {
info$ = new LiveData<ShareInfoType | undefined | null>(null); info$ = new LiveData<ShareInfoType | undefined | null>(null);
@@ -70,7 +70,7 @@ export class ShareInfo extends Entity {
return this.isRevalidating$.waitFor(v => v === false, signal); return this.isRevalidating$.waitFor(v => v === false, signal);
} }
async enableShare(mode: PublicPageMode) { async enableShare(mode: PublicDocMode) {
await this.store.enableSharePage( await this.store.enableSharePage(
this.workspaceService.workspace.id, this.workspaceService.workspace.id,
this.docService.doc.id, this.docService.doc.id,
@@ -79,7 +79,7 @@ export class ShareInfo extends Entity {
await this.waitForRevalidation(); await this.waitForRevalidation();
} }
async changeShare(mode: PublicPageMode) { async changeShare(mode: PublicDocMode) {
await this.enableShare(mode); await this.enableShare(mode);
} }

View File

@@ -20,6 +20,6 @@ export class ShareDocsStore extends Store {
signal, signal,
}, },
}); });
return data.workspace.publicPages; return data.workspace.publicDocs;
} }
} }

View File

@@ -1,4 +1,4 @@
import type { PublicPageMode } from '@affine/graphql'; import type { PublicDocMode } from '@affine/graphql';
import { import {
getWorkspacePublicPageByIdQuery, getWorkspacePublicPageByIdQuery,
publishPageMutation, publishPageMutation,
@@ -31,13 +31,13 @@ export class ShareStore extends Store {
signal, signal,
}, },
}); });
return data.workspace.publicPage ?? undefined; return data.workspace.publicDoc ?? undefined;
} }
async enableSharePage( async enableSharePage(
workspaceId: string, workspaceId: string,
pageId: string, pageId: string,
docMode?: PublicPageMode, docMode?: PublicDocMode,
signal?: AbortSignal signal?: AbortSignal
) { ) {
if (!this.workspaceServerService.server) { if (!this.workspaceServerService.server) {

View File

@@ -1,6 +1,6 @@
query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) { query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) {
workspace(id: $workspaceId) { workspace(id: $workspaceId) {
publicPage(pageId: $pageId) { publicDoc(docId: $pageId) {
id id
mode mode
} }

View File

@@ -1,6 +1,6 @@
query getWorkspacePublicPages($workspaceId: String!) { query getWorkspacePublicPages($workspaceId: String!) {
workspace(id: $workspaceId) { workspace(id: $workspaceId) {
publicPages { publicDocs {
id id
mode mode
} }

View File

@@ -752,7 +752,7 @@ export const getWorkspacePublicPageByIdQuery = {
query: ` query: `
query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) { query getWorkspacePublicPageById($workspaceId: String!, $pageId: String!) {
workspace(id: $workspaceId) { workspace(id: $workspaceId) {
publicPage(pageId: $pageId) { publicDoc(docId: $pageId) {
id id
mode mode
} }
@@ -768,7 +768,7 @@ export const getWorkspacePublicPagesQuery = {
query: ` query: `
query getWorkspacePublicPages($workspaceId: String!) { query getWorkspacePublicPages($workspaceId: String!) {
workspace(id: $workspaceId) { workspace(id: $workspaceId) {
publicPages { publicDocs {
id id
mode mode
} }
@@ -937,11 +937,11 @@ query prices {
export const publishPageMutation = { export const publishPageMutation = {
id: 'publishPageMutation' as const, id: 'publishPageMutation' as const,
operationName: 'publishPage', operationName: 'publishPage',
definitionName: 'publishPage', definitionName: 'publishDoc',
containsFile: false, containsFile: false,
query: ` query: `
mutation publishPage($workspaceId: String!, $pageId: String!, $mode: PublicPageMode = Page) { mutation publishPage($workspaceId: String!, $pageId: String!, $mode: PublicDocMode = Page) {
publishPage(workspaceId: $workspaceId, pageId: $pageId, mode: $mode) { publishDoc(workspaceId: $workspaceId, docId: $pageId, mode: $mode) {
id id
mode mode
} }
@@ -1033,11 +1033,11 @@ mutation revokeMemberPermission($workspaceId: String!, $userId: String!) {
export const revokePublicPageMutation = { export const revokePublicPageMutation = {
id: 'revokePublicPageMutation' as const, id: 'revokePublicPageMutation' as const,
operationName: 'revokePublicPage', operationName: 'revokePublicPage',
definitionName: 'revokePublicPage', definitionName: 'revokePublicDoc',
containsFile: false, containsFile: false,
query: ` query: `
mutation revokePublicPage($workspaceId: String!, $pageId: String!) { mutation revokePublicPage($workspaceId: String!, $pageId: String!) {
revokePublicPage(workspaceId: $workspaceId, pageId: $pageId) { revokePublicDoc(workspaceId: $workspaceId, docId: $pageId) {
id id
mode mode
public public

View File

@@ -1,9 +1,9 @@
mutation publishPage( mutation publishPage(
$workspaceId: String! $workspaceId: String!
$pageId: String! $pageId: String!
$mode: PublicPageMode = Page $mode: PublicDocMode = Page
) { ) {
publishPage(workspaceId: $workspaceId, pageId: $pageId, mode: $mode) { publishDoc(workspaceId: $workspaceId, docId: $pageId, mode: $mode) {
id id
mode mode
} }

View File

@@ -1,5 +1,5 @@
mutation revokePublicPage($workspaceId: String!, $pageId: String!) { mutation revokePublicPage($workspaceId: String!, $pageId: String!) {
revokePublicPage(workspaceId: $workspaceId, pageId: $pageId) { revokePublicDoc(workspaceId: $workspaceId, docId: $pageId) {
id id
mode mode
public public

View File

@@ -253,6 +253,23 @@ export interface DocNotFoundDataType {
spaceId: Scalars['String']['output']; 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 */ /** User permission in doc */
export enum DocRole { export enum DocRole {
Editor = 'Editor', Editor = 'Editor',
@@ -264,10 +281,17 @@ export enum DocRole {
export interface DocType { export interface DocType {
__typename?: 'DocType'; __typename?: 'DocType';
/** paginated doc granted users list */
grantedUsersList: PaginatedGrantedDocUserType;
id: Scalars['String']['output']; id: Scalars['String']['output'];
permissions: RolePermissions; mode: PublicDocMode;
permissions: DocPermissions;
public: Scalars['Boolean']['output']; public: Scalars['Boolean']['output'];
role: DocRole; workspaceId: Scalars['String']['output'];
}
export interface DocTypeGrantedUsersListArgs {
pagination: PaginationInput;
} }
export interface EditorType { export interface EditorType {
@@ -337,16 +361,18 @@ export enum ErrorNames {
COPILOT_SESSION_NOT_FOUND = 'COPILOT_SESSION_NOT_FOUND', COPILOT_SESSION_NOT_FOUND = 'COPILOT_SESSION_NOT_FOUND',
CUSTOMER_PORTAL_CREATE_FAILED = 'CUSTOMER_PORTAL_CREATE_FAILED', CUSTOMER_PORTAL_CREATE_FAILED = 'CUSTOMER_PORTAL_CREATE_FAILED',
DOC_ACCESS_DENIED = 'DOC_ACCESS_DENIED', 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_HISTORY_NOT_FOUND = 'DOC_HISTORY_NOT_FOUND',
DOC_IS_NOT_PUBLIC = 'DOC_IS_NOT_PUBLIC',
DOC_NOT_FOUND = 'DOC_NOT_FOUND', DOC_NOT_FOUND = 'DOC_NOT_FOUND',
EARLY_ACCESS_REQUIRED = 'EARLY_ACCESS_REQUIRED', EARLY_ACCESS_REQUIRED = 'EARLY_ACCESS_REQUIRED',
EMAIL_ALREADY_USED = 'EMAIL_ALREADY_USED', EMAIL_ALREADY_USED = 'EMAIL_ALREADY_USED',
EMAIL_TOKEN_NOT_FOUND = 'EMAIL_TOKEN_NOT_FOUND', EMAIL_TOKEN_NOT_FOUND = 'EMAIL_TOKEN_NOT_FOUND',
EMAIL_VERIFICATION_REQUIRED = 'EMAIL_VERIFICATION_REQUIRED', EMAIL_VERIFICATION_REQUIRED = 'EMAIL_VERIFICATION_REQUIRED',
EXPECT_TO_GRANT_DOC_USER_ROLES = 'EXPECT_TO_GRANT_DOC_USER_ROLES', 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_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', EXPECT_TO_UPDATE_DOC_USER_ROLE = 'EXPECT_TO_UPDATE_DOC_USER_ROLE',
FAILED_TO_CHECKOUT = 'FAILED_TO_CHECKOUT', FAILED_TO_CHECKOUT = 'FAILED_TO_CHECKOUT',
FAILED_TO_SAVE_UPDATES = 'FAILED_TO_SAVE_UPDATES', FAILED_TO_SAVE_UPDATES = 'FAILED_TO_SAVE_UPDATES',
@@ -375,7 +401,6 @@ export enum ErrorNames {
NO_COPILOT_PROVIDER_AVAILABLE = 'NO_COPILOT_PROVIDER_AVAILABLE', NO_COPILOT_PROVIDER_AVAILABLE = 'NO_COPILOT_PROVIDER_AVAILABLE',
OAUTH_ACCOUNT_ALREADY_CONNECTED = 'OAUTH_ACCOUNT_ALREADY_CONNECTED', OAUTH_ACCOUNT_ALREADY_CONNECTED = 'OAUTH_ACCOUNT_ALREADY_CONNECTED',
OAUTH_STATE_EXPIRED = 'OAUTH_STATE_EXPIRED', OAUTH_STATE_EXPIRED = 'OAUTH_STATE_EXPIRED',
PAGE_IS_NOT_PUBLIC = 'PAGE_IS_NOT_PUBLIC',
PASSWORD_REQUIRED = 'PASSWORD_REQUIRED', PASSWORD_REQUIRED = 'PASSWORD_REQUIRED',
QUERY_TOO_LONG = 'QUERY_TOO_LONG', QUERY_TOO_LONG = 'QUERY_TOO_LONG',
RUNTIME_CONFIG_NOT_FOUND = 'RUNTIME_CONFIG_NOT_FOUND', RUNTIME_CONFIG_NOT_FOUND = 'RUNTIME_CONFIG_NOT_FOUND',
@@ -453,23 +478,16 @@ export interface GrantDocUserRolesInput {
workspaceId: Scalars['String']['input']; workspaceId: Scalars['String']['input'];
} }
export interface GrantedDocUserEdge {
__typename?: 'GrantedDocUserEdge';
cursor: Scalars['String']['output'];
user: GrantedDocUserType;
}
export interface GrantedDocUserType { export interface GrantedDocUserType {
__typename?: 'GrantedDocUserType'; __typename?: 'GrantedDocUserType';
role: DocRole; role: DocRole;
user: UserType; user: PublicUserType;
} }
export interface GrantedDocUsersConnection { export interface GrantedDocUserTypeEdge {
__typename?: 'GrantedDocUsersConnection'; __typename?: 'GrantedDocUserTypeEdge';
edges: Array<GrantedDocUserEdge>; cursor: Scalars['String']['output'];
pageInfo: PageInfo; node: GrantedDocUserType;
totalCount: Scalars['Int']['output'];
} }
export interface InvalidEmailDataType { export interface InvalidEmailDataType {
@@ -688,7 +706,9 @@ export interface Mutation {
invite: Scalars['String']['output']; invite: Scalars['String']['output'];
inviteBatch: Array<InviteResult>; inviteBatch: Array<InviteResult>;
leaveWorkspace: Scalars['Boolean']['output']; leaveWorkspace: Scalars['Boolean']['output'];
publishPage: WorkspacePage; publishDoc: DocType;
/** @deprecated use publishDoc instead */
publishPage: DocType;
recoverDoc: Scalars['DateTime']['output']; recoverDoc: Scalars['DateTime']['output'];
releaseDeletedBlobs: Scalars['Boolean']['output']; releaseDeletedBlobs: Scalars['Boolean']['output'];
/** Remove user avatar */ /** Remove user avatar */
@@ -698,21 +718,20 @@ export interface Mutation {
revoke: Scalars['Boolean']['output']; revoke: Scalars['Boolean']['output'];
revokeDocUserRoles: Scalars['Boolean']['output']; revokeDocUserRoles: Scalars['Boolean']['output'];
revokeInviteLink: Scalars['Boolean']['output']; revokeInviteLink: Scalars['Boolean']['output'];
/** @deprecated use revokePublicPage */ revokePublicDoc: DocType;
revokePage: Scalars['Boolean']['output']; /** @deprecated use revokePublicDoc instead */
revokePublicPage: WorkspacePage; revokePublicPage: DocType;
sendChangeEmail: Scalars['Boolean']['output']; sendChangeEmail: Scalars['Boolean']['output'];
sendChangePasswordEmail: Scalars['Boolean']['output']; sendChangePasswordEmail: Scalars['Boolean']['output'];
sendSetPasswordEmail: Scalars['Boolean']['output']; sendSetPasswordEmail: Scalars['Boolean']['output'];
sendVerifyChangeEmail: Scalars['Boolean']['output']; sendVerifyChangeEmail: Scalars['Boolean']['output'];
sendVerifyEmail: Scalars['Boolean']['output']; sendVerifyEmail: Scalars['Boolean']['output'];
setBlob: Scalars['String']['output']; setBlob: Scalars['String']['output'];
/** @deprecated renamed to publishPage */
sharePage: Scalars['Boolean']['output'];
/** Update a copilot prompt */ /** Update a copilot prompt */
updateCopilotPrompt: CopilotPromptType; updateCopilotPrompt: CopilotPromptType;
/** Update a chat session */ /** Update a chat session */
updateCopilotSession: Scalars['String']['output']; updateCopilotSession: Scalars['String']['output'];
updateDocDefaultRole: Scalars['Boolean']['output'];
updateDocUserRole: Scalars['Boolean']['output']; updateDocUserRole: Scalars['Boolean']['output'];
updateProfile: UserType; updateProfile: UserType;
/** update server runtime configurable setting */ /** update server runtime configurable setting */
@@ -867,8 +886,14 @@ export interface MutationLeaveWorkspaceArgs {
workspaceName?: InputMaybe<Scalars['String']['input']>; workspaceName?: InputMaybe<Scalars['String']['input']>;
} }
export interface MutationPublishDocArgs {
docId: Scalars['String']['input'];
mode?: InputMaybe<PublicDocMode>;
workspaceId: Scalars['String']['input'];
}
export interface MutationPublishPageArgs { export interface MutationPublishPageArgs {
mode?: InputMaybe<PublicPageMode>; mode?: InputMaybe<PublicDocMode>;
pageId: Scalars['String']['input']; pageId: Scalars['String']['input'];
workspaceId: Scalars['String']['input']; workspaceId: Scalars['String']['input'];
} }
@@ -900,21 +925,20 @@ export interface MutationRevokeArgs {
} }
export interface MutationRevokeDocUserRolesArgs { export interface MutationRevokeDocUserRolesArgs {
docId: Scalars['String']['input']; input: RevokeDocUserRolesInput;
userIds: Array<Scalars['String']['input']>;
} }
export interface MutationRevokeInviteLinkArgs { export interface MutationRevokeInviteLinkArgs {
workspaceId: Scalars['String']['input']; workspaceId: Scalars['String']['input'];
} }
export interface MutationRevokePageArgs { export interface MutationRevokePublicDocArgs {
pageId: Scalars['String']['input']; docId: Scalars['String']['input'];
workspaceId: Scalars['String']['input']; workspaceId: Scalars['String']['input'];
} }
export interface MutationRevokePublicPageArgs { export interface MutationRevokePublicPageArgs {
pageId: Scalars['String']['input']; docId: Scalars['String']['input'];
workspaceId: Scalars['String']['input']; workspaceId: Scalars['String']['input'];
} }
@@ -948,11 +972,6 @@ export interface MutationSetBlobArgs {
workspaceId: Scalars['String']['input']; workspaceId: Scalars['String']['input'];
} }
export interface MutationSharePageArgs {
pageId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
}
export interface MutationUpdateCopilotPromptArgs { export interface MutationUpdateCopilotPromptArgs {
messages: Array<CopilotPromptMessageInput>; messages: Array<CopilotPromptMessageInput>;
name: Scalars['String']['input']; name: Scalars['String']['input'];
@@ -962,10 +981,12 @@ export interface MutationUpdateCopilotSessionArgs {
options: UpdateChatSessionInput; options: UpdateChatSessionInput;
} }
export interface MutationUpdateDocDefaultRoleArgs {
input: UpdateDocDefaultRoleInput;
}
export interface MutationUpdateDocUserRoleArgs { export interface MutationUpdateDocUserRoleArgs {
docId: Scalars['String']['input']; input: UpdateDocUserRoleInput;
role: DocRole;
userId: Scalars['String']['input'];
} }
export interface MutationUpdateProfileArgs { export interface MutationUpdateProfileArgs {
@@ -1021,15 +1042,6 @@ export enum OAuthProviderType {
OIDC = 'OIDC', 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 { export interface PageInfo {
__typename?: 'PageInfo'; __typename?: 'PageInfo';
endCursor: Maybe<Scalars['String']['output']>; endCursor: Maybe<Scalars['String']['output']>;
@@ -1038,6 +1050,22 @@ export interface PageInfo {
startCursor: Maybe<Scalars['String']['output']>; 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 { export interface PasswordLimitsType {
__typename?: 'PasswordLimitsType'; __typename?: 'PasswordLimitsType';
maxLength: Scalars['Int']['output']; maxLength: Scalars['Int']['output'];
@@ -1052,12 +1080,20 @@ export enum Permission {
Owner = 'Owner', Owner = 'Owner',
} }
/** The mode which the public page default in */ /** The mode which the public doc default in */
export enum PublicPageMode { export enum PublicDocMode {
Edgeless = 'Edgeless', Edgeless = 'Edgeless',
Page = 'Page', 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 { export interface Query {
__typename?: 'Query'; __typename?: 'Query';
/** @deprecated use `user.quotaUsage` instead */ /** @deprecated use `user.quotaUsage` instead */
@@ -1166,34 +1202,10 @@ export interface RemoveAvatar {
success: Scalars['Boolean']['output']; success: Scalars['Boolean']['output'];
} }
export interface RolePermissions { export interface RevokeDocUserRolesInput {
__typename?: 'RolePermissions'; docId: Scalars['String']['input'];
Doc_Copy: Scalars['Boolean']['output']; userIds: Array<Scalars['String']['input']>;
Doc_Delete: Scalars['Boolean']['output']; workspaceId: Scalars['String']['input'];
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 RuntimeConfigNotFoundDataType { export interface RuntimeConfigNotFoundDataType {
@@ -1395,6 +1407,19 @@ export interface UpdateChatSessionInput {
sessionId: Scalars['String']['input']; 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 { export interface UpdateUserInput {
/** User name */ /** User name */
name?: InputMaybe<Scalars['String']['input']>; name?: InputMaybe<Scalars['String']['input']>;
@@ -1514,14 +1539,6 @@ export interface WorkspaceMembersExceedLimitToDowngradeDataType {
limit: Scalars['Int']['output']; 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 { export interface WorkspacePageMeta {
__typename?: 'WorkspacePageMeta'; __typename?: 'WorkspacePageMeta';
createdAt: Scalars['DateTime']['output']; createdAt: Scalars['DateTime']['output'];
@@ -1591,6 +1608,8 @@ export interface WorkspaceType {
blobsSize: Scalars['Int']['output']; blobsSize: Scalars['Int']['output'];
/** Workspace created date */ /** Workspace created date */
createdAt: Scalars['DateTime']['output']; createdAt: Scalars['DateTime']['output'];
/** Get get with given id */
doc: DocType;
/** Enable AI */ /** Enable AI */
enableAi: Scalars['Boolean']['output']; enableAi: Scalars['Boolean']['output'];
/** Enable url previous when sharing */ /** Enable url previous when sharing */
@@ -1612,33 +1631,35 @@ export interface WorkspaceType {
members: Array<InviteUserType>; members: Array<InviteUserType>;
/** Owner of workspace */ /** Owner of workspace */
owner: UserType; owner: UserType;
/** Page granted users list */
pageGrantedUsersList: GrantedDocUsersConnection;
/** Cloud page metadata of workspace */ /** Cloud page metadata of workspace */
pageMeta: WorkspacePageMeta; pageMeta: WorkspacePageMeta;
/** Check if current user has permission to access the page */
pagePermission: DocType;
/** is Public workspace */ /** is Public workspace */
public: Scalars['Boolean']['output']; public: Scalars['Boolean']['output'];
/** Get public page of a workspace by page id. */ /** Get public page of a workspace by page id. */
publicPage: Maybe<WorkspacePage>; publicDoc: Maybe<DocType>;
/** Public pages of a workspace */ /** Get public docs of a workspace */
publicPages: Array<WorkspacePage>; 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 of workspace */
quota: WorkspaceQuotaType; quota: WorkspaceQuotaType;
/** Role of current signed in user in workspace */ /** Role of current signed in user in workspace */
role: Permission; role: Permission;
/**
* Shared pages of workspace
* @deprecated use WorkspaceType.publicPages
*/
sharedPages: Array<Scalars['String']['output']>;
/** The team subscription of the workspace, if exists. */ /** The team subscription of the workspace, if exists. */
subscription: Maybe<SubscriptionType>; subscription: Maybe<SubscriptionType>;
/** if workspace is team workspace */ /** if workspace is team workspace */
team: Scalars['Boolean']['output']; team: Scalars['Boolean']['output'];
} }
export interface WorkspaceTypeDocArgs {
docId: Scalars['String']['input'];
}
export interface WorkspaceTypeHistoriesArgs { export interface WorkspaceTypeHistoriesArgs {
before?: InputMaybe<Scalars['DateTime']['input']>; before?: InputMaybe<Scalars['DateTime']['input']>;
guid: Scalars['String']['input']; guid: Scalars['String']['input'];
@@ -1656,17 +1677,12 @@ export interface WorkspaceTypeMembersArgs {
take?: InputMaybe<Scalars['Int']['input']>; take?: InputMaybe<Scalars['Int']['input']>;
} }
export interface WorkspaceTypePageGrantedUsersListArgs {
pageGrantedUsersInput: PageGrantedUsersInput;
pageId: Scalars['String']['input'];
}
export interface WorkspaceTypePageMetaArgs { export interface WorkspaceTypePageMetaArgs {
pageId: Scalars['String']['input']; pageId: Scalars['String']['input'];
} }
export interface WorkspaceTypePagePermissionArgs { export interface WorkspaceTypePublicDocArgs {
pageId: Scalars['String']['input']; docId: Scalars['String']['input'];
} }
export interface WorkspaceTypePublicPageArgs { export interface WorkspaceTypePublicPageArgs {
@@ -2357,10 +2373,10 @@ export type GetWorkspacePublicPageByIdQuery = {
__typename?: 'Query'; __typename?: 'Query';
workspace: { workspace: {
__typename?: 'WorkspaceType'; __typename?: 'WorkspaceType';
publicPage: { publicDoc: {
__typename?: 'WorkspacePage'; __typename?: 'DocType';
id: string; id: string;
mode: PublicPageMode; mode: PublicDocMode;
} | null; } | null;
}; };
}; };
@@ -2373,10 +2389,10 @@ export type GetWorkspacePublicPagesQuery = {
__typename?: 'Query'; __typename?: 'Query';
workspace: { workspace: {
__typename?: 'WorkspaceType'; __typename?: 'WorkspaceType';
publicPages: Array<{ publicDocs: Array<{
__typename?: 'WorkspacePage'; __typename?: 'DocType';
id: string; id: string;
mode: PublicPageMode; mode: PublicDocMode;
}>; }>;
}; };
}; };
@@ -2527,16 +2543,12 @@ export type PricesQuery = {
export type PublishPageMutationVariables = Exact<{ export type PublishPageMutationVariables = Exact<{
workspaceId: Scalars['String']['input']; workspaceId: Scalars['String']['input'];
pageId: Scalars['String']['input']; pageId: Scalars['String']['input'];
mode?: InputMaybe<PublicPageMode>; mode?: InputMaybe<PublicDocMode>;
}>; }>;
export type PublishPageMutation = { export type PublishPageMutation = {
__typename?: 'Mutation'; __typename?: 'Mutation';
publishPage: { publishDoc: { __typename?: 'DocType'; id: string; mode: PublicDocMode };
__typename?: 'WorkspacePage';
id: string;
mode: PublicPageMode;
};
}; };
export type QuotaQueryVariables = Exact<{ [key: string]: never }>; export type QuotaQueryVariables = Exact<{ [key: string]: never }>;
@@ -2618,10 +2630,10 @@ export type RevokePublicPageMutationVariables = Exact<{
export type RevokePublicPageMutation = { export type RevokePublicPageMutation = {
__typename?: 'Mutation'; __typename?: 'Mutation';
revokePublicPage: { revokePublicDoc: {
__typename?: 'WorkspacePage'; __typename?: 'DocType';
id: string; id: string;
mode: PublicPageMode; mode: PublicDocMode;
public: boolean; public: boolean;
}; };
}; };