mirror of
https://github.com/docmost/docmost.git
synced 2026-05-08 23:33:09 +08:00
fix: scope synced blocks to workspace, gate unsync on edit permission
This commit is contained in:
@@ -165,7 +165,7 @@ export class PersistenceExtension implements Extension {
|
||||
}
|
||||
|
||||
if (page) {
|
||||
await this.syncTransclusion(pageId, tiptapJson);
|
||||
await this.syncTransclusion(pageId, page.workspaceId, tiptapJson);
|
||||
}
|
||||
|
||||
if (page) {
|
||||
@@ -250,10 +250,15 @@ export class PersistenceExtension implements Extension {
|
||||
*/
|
||||
private async syncTransclusion(
|
||||
pageId: string,
|
||||
workspaceId: string,
|
||||
tiptapJson: unknown,
|
||||
): Promise<void> {
|
||||
try {
|
||||
await this.transclusionService.syncPageTransclusions(pageId, tiptapJson);
|
||||
await this.transclusionService.syncPageTransclusions(
|
||||
pageId,
|
||||
workspaceId,
|
||||
tiptapJson,
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
{ err, pageId },
|
||||
@@ -261,7 +266,11 @@ export class PersistenceExtension implements Extension {
|
||||
);
|
||||
}
|
||||
try {
|
||||
await this.transclusionService.syncPageReferences(pageId, tiptapJson);
|
||||
await this.transclusionService.syncPageReferences(
|
||||
pageId,
|
||||
workspaceId,
|
||||
tiptapJson,
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
{ err, pageId },
|
||||
|
||||
@@ -677,7 +677,11 @@ export class PageService {
|
||||
// pages never have prior rows so we can skip the diff and just bulk-insert.
|
||||
try {
|
||||
await this.transclusionService.insertTransclusionsForPages(
|
||||
insertablePages.map((p) => ({ id: p.id, content: p.content })),
|
||||
insertablePages.map((p) => ({
|
||||
id: p.id,
|
||||
workspaceId: p.workspaceId,
|
||||
content: p.content,
|
||||
})),
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
@@ -688,7 +692,11 @@ export class PageService {
|
||||
|
||||
try {
|
||||
await this.transclusionService.insertReferencesForPages(
|
||||
insertablePages.map((p) => ({ id: p.id, content: p.content })),
|
||||
insertablePages.map((p) => ({
|
||||
id: p.id,
|
||||
workspaceId: p.workspaceId,
|
||||
content: p.content,
|
||||
})),
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
|
||||
@@ -25,10 +25,10 @@ describe('TransclusionController.lookup', () => {
|
||||
controller = module.get(TransclusionController);
|
||||
});
|
||||
|
||||
const user = { id: 'u1' } as any;
|
||||
const user = { id: 'u1', workspaceId: 'w1' } as any;
|
||||
const ref = { sourcePageId: 'p1', transclusionId: 'e1' };
|
||||
|
||||
it('passes the references and viewer id through to the service and returns its result', async () => {
|
||||
it('passes the references, viewer id and workspace id through to the service and returns its result', async () => {
|
||||
service.lookup.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
@@ -43,6 +43,6 @@ describe('TransclusionController.lookup', () => {
|
||||
const out = await controller.lookup({ references: [ref] } as any, user);
|
||||
expect(out.items[0]).not.toHaveProperty('status');
|
||||
expect((out.items[0] as any).content).toEqual({ type: 'doc' });
|
||||
expect(service.lookup).toHaveBeenCalledWith([ref], 'u1');
|
||||
expect(service.lookup).toHaveBeenCalledWith([ref], 'u1', 'w1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
||||
import { PagePermissionRepo } from '@docmost/db/repos/page/page-permission.repo';
|
||||
import { AttachmentRepo } from '@docmost/db/repos/attachment/attachment.repo';
|
||||
import { StorageService } from '../../../../integrations/storage/storage.service';
|
||||
import { PageAccessService } from '../../page-access/page-access.service';
|
||||
|
||||
describe('TransclusionService.syncPageTransclusions', () => {
|
||||
let service: TransclusionService;
|
||||
@@ -27,6 +28,7 @@ describe('TransclusionService.syncPageTransclusions', () => {
|
||||
{ provide: PagePermissionRepo, useValue: {} },
|
||||
{ provide: AttachmentRepo, useValue: {} },
|
||||
{ provide: StorageService, useValue: {} },
|
||||
{ provide: PageAccessService, useValue: {} },
|
||||
],
|
||||
}).compile();
|
||||
service = module.get(TransclusionService);
|
||||
@@ -34,6 +36,7 @@ describe('TransclusionService.syncPageTransclusions', () => {
|
||||
});
|
||||
|
||||
const pageId = '00000000-0000-0000-0000-000000000001';
|
||||
const workspaceId = '00000000-0000-0000-0000-000000000099';
|
||||
|
||||
it('inserts new transclusions that did not exist before', async () => {
|
||||
repo.findByPageId.mockResolvedValue([]);
|
||||
@@ -48,7 +51,7 @@ describe('TransclusionService.syncPageTransclusions', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const result = await service.syncPageTransclusions(pageId, pm);
|
||||
const result = await service.syncPageTransclusions(pageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 1, updated: 0, deleted: 0 });
|
||||
expect(repo.insert).toHaveBeenCalledTimes(1);
|
||||
@@ -91,7 +94,7 @@ describe('TransclusionService.syncPageTransclusions', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const result = await service.syncPageTransclusions(pageId, pm);
|
||||
const result = await service.syncPageTransclusions(pageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 0, updated: 1, deleted: 0 });
|
||||
expect(repo.update).toHaveBeenCalledWith(
|
||||
@@ -128,7 +131,7 @@ describe('TransclusionService.syncPageTransclusions', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const result = await service.syncPageTransclusions(pageId, pm);
|
||||
const result = await service.syncPageTransclusions(pageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 0, updated: 0, deleted: 0 });
|
||||
expect(repo.update).not.toHaveBeenCalled();
|
||||
@@ -147,7 +150,7 @@ describe('TransclusionService.syncPageTransclusions', () => {
|
||||
]);
|
||||
const pm = { type: 'doc', content: [{ type: 'paragraph' }] };
|
||||
|
||||
const result = await service.syncPageTransclusions(pageId, pm);
|
||||
const result = await service.syncPageTransclusions(pageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 0, updated: 0, deleted: 1 });
|
||||
expect(repo.deleteByPageAndTransclusionIds).toHaveBeenCalledWith(
|
||||
@@ -159,7 +162,7 @@ describe('TransclusionService.syncPageTransclusions', () => {
|
||||
|
||||
it('handles empty doc → noop', async () => {
|
||||
repo.findByPageId.mockResolvedValue([]);
|
||||
const result = await service.syncPageTransclusions(pageId, null);
|
||||
const result = await service.syncPageTransclusions(pageId, workspaceId, null);
|
||||
expect(result).toEqual({ inserted: 0, updated: 0, deleted: 0 });
|
||||
expect(repo.insert).not.toHaveBeenCalled();
|
||||
expect(repo.update).not.toHaveBeenCalled();
|
||||
@@ -187,6 +190,7 @@ describe('TransclusionService.syncPageReferences', () => {
|
||||
{ provide: PagePermissionRepo, useValue: {} },
|
||||
{ provide: AttachmentRepo, useValue: {} },
|
||||
{ provide: StorageService, useValue: {} },
|
||||
{ provide: PageAccessService, useValue: {} },
|
||||
],
|
||||
}).compile();
|
||||
service = module.get(TransclusionService);
|
||||
@@ -194,6 +198,7 @@ describe('TransclusionService.syncPageReferences', () => {
|
||||
});
|
||||
|
||||
const referencePageId = '00000000-0000-0000-0000-000000000001';
|
||||
const workspaceId = '00000000-0000-0000-0000-000000000099';
|
||||
|
||||
it('inserts new loose references, no deletes when none existed', async () => {
|
||||
refRepo.findByReferencePageId.mockResolvedValue([]);
|
||||
@@ -211,17 +216,19 @@ describe('TransclusionService.syncPageReferences', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const result = await service.syncPageReferences(referencePageId, pm);
|
||||
const result = await service.syncPageReferences(referencePageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 2, deleted: 0 });
|
||||
expect(refRepo.insertMany).toHaveBeenCalledWith(
|
||||
[
|
||||
{
|
||||
workspaceId,
|
||||
referencePageId,
|
||||
sourcePageId: 'p1',
|
||||
transclusionId: 'e1',
|
||||
},
|
||||
{
|
||||
workspaceId,
|
||||
referencePageId,
|
||||
sourcePageId: 'p2',
|
||||
transclusionId: 'e2',
|
||||
@@ -250,7 +257,7 @@ describe('TransclusionService.syncPageReferences', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const result = await service.syncPageReferences(referencePageId, pm);
|
||||
const result = await service.syncPageReferences(referencePageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 0, deleted: 0 });
|
||||
expect(refRepo.insertMany).not.toHaveBeenCalled();
|
||||
@@ -268,7 +275,7 @@ describe('TransclusionService.syncPageReferences', () => {
|
||||
]);
|
||||
const pm = { type: 'doc', content: [{ type: 'paragraph' }] };
|
||||
|
||||
const result = await service.syncPageReferences(referencePageId, pm);
|
||||
const result = await service.syncPageReferences(referencePageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 0, deleted: 1 });
|
||||
expect(refRepo.deleteByReferenceAndKeys).toHaveBeenCalledWith(
|
||||
@@ -304,7 +311,7 @@ describe('TransclusionService.syncPageReferences', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const result = await service.syncPageReferences(referencePageId, pm);
|
||||
const result = await service.syncPageReferences(referencePageId, workspaceId, pm);
|
||||
|
||||
expect(result).toEqual({ inserted: 0, deleted: 0 });
|
||||
expect(refRepo.insertMany).not.toHaveBeenCalled();
|
||||
|
||||
@@ -24,7 +24,8 @@ export class TransclusionController {
|
||||
async lookup(@Body() dto: LookupDto, @AuthUser() user: User) {
|
||||
return this.transclusionService.lookup(
|
||||
dto.references,
|
||||
user?.id ?? null,
|
||||
user.id,
|
||||
user.workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,6 +39,7 @@ export class TransclusionController {
|
||||
sourcePageId: dto.sourcePageId,
|
||||
transclusionId: dto.transclusionId,
|
||||
viewerUserId: user.id,
|
||||
workspaceId: user.workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,7 +53,7 @@ export class TransclusionController {
|
||||
dto.referencePageId,
|
||||
dto.sourcePageId,
|
||||
dto.transclusionId,
|
||||
user.id,
|
||||
user,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
} from './utils/transclusion-prosemirror.util';
|
||||
import { rewriteAttachmentsForUnsync } from './utils/transclusion-unsync.util';
|
||||
import { TransclusionLookup } from './transclusion.types';
|
||||
import { Page } from '@docmost/db/types/entity.types';
|
||||
import { Page, User } from '@docmost/db/types/entity.types';
|
||||
import { PageAccessService } from '../page-access/page-access.service';
|
||||
|
||||
type ReferencingPageInfo = {
|
||||
id: string;
|
||||
@@ -41,10 +42,12 @@ export class TransclusionService {
|
||||
private readonly pagePermissionRepo: PagePermissionRepo,
|
||||
private readonly attachmentRepo: AttachmentRepo,
|
||||
private readonly storageService: StorageService,
|
||||
private readonly pageAccessService: PageAccessService,
|
||||
) {}
|
||||
|
||||
async syncPageTransclusions(
|
||||
pageId: string,
|
||||
workspaceId: string,
|
||||
pmJson: unknown,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<{ inserted: number; updated: number; deleted: number }> {
|
||||
@@ -63,6 +66,7 @@ export class TransclusionService {
|
||||
if (!prev) {
|
||||
await this.pageTransclusionsRepo.insert(
|
||||
{
|
||||
workspaceId,
|
||||
pageId,
|
||||
transclusionId: d.transclusionId,
|
||||
content: d.content as any,
|
||||
@@ -102,6 +106,7 @@ export class TransclusionService {
|
||||
|
||||
async syncPageReferences(
|
||||
referencePageId: string,
|
||||
workspaceId: string,
|
||||
pmJson: unknown,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<{ inserted: number; deleted: number }> {
|
||||
@@ -121,6 +126,7 @@ export class TransclusionService {
|
||||
const toInsert = desired
|
||||
.filter((d) => !existingKeys.has(keyOf(d)))
|
||||
.map((d) => ({
|
||||
workspaceId,
|
||||
referencePageId,
|
||||
sourcePageId: d.sourcePageId,
|
||||
transclusionId: d.transclusionId,
|
||||
@@ -156,7 +162,7 @@ export class TransclusionService {
|
||||
* (e.g. duplication, import) where there is nothing to diff against.
|
||||
*/
|
||||
async insertTransclusionsForPages(
|
||||
pages: Array<{ id: string; content: unknown }>,
|
||||
pages: Array<{ id: string; workspaceId: string; content: unknown }>,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<{ inserted: number }> {
|
||||
const rows: Parameters<PageTransclusionsRepo['insertMany']>[0] = [];
|
||||
@@ -164,6 +170,7 @@ export class TransclusionService {
|
||||
const snapshots = collectTransclusionsFromPmJson(page.content);
|
||||
for (const s of snapshots) {
|
||||
rows.push({
|
||||
workspaceId: page.workspaceId,
|
||||
pageId: page.id,
|
||||
transclusionId: s.transclusionId,
|
||||
content: s.content as any,
|
||||
@@ -181,10 +188,11 @@ export class TransclusionService {
|
||||
* (duplication, import) where there is nothing to diff against.
|
||||
*/
|
||||
async insertReferencesForPages(
|
||||
pages: Array<{ id: string; content: unknown }>,
|
||||
pages: Array<{ id: string; workspaceId: string; content: unknown }>,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<{ inserted: number }> {
|
||||
const rows: Array<{
|
||||
workspaceId: string;
|
||||
referencePageId: string;
|
||||
sourcePageId: string;
|
||||
transclusionId: string;
|
||||
@@ -193,6 +201,7 @@ export class TransclusionService {
|
||||
const refs = collectReferencesFromPmJson(page.content);
|
||||
for (const r of refs) {
|
||||
rows.push({
|
||||
workspaceId: page.workspaceId,
|
||||
referencePageId: page.id,
|
||||
sourcePageId: r.sourcePageId,
|
||||
transclusionId: r.transclusionId,
|
||||
@@ -206,23 +215,22 @@ export class TransclusionService {
|
||||
|
||||
async lookup(
|
||||
references: Array<{ sourcePageId: string; transclusionId: string }>,
|
||||
viewerUserId: string | null,
|
||||
viewerUserId: string,
|
||||
workspaceId: string,
|
||||
): Promise<{ items: TransclusionLookup[] }> {
|
||||
if (references.length === 0) return { items: [] };
|
||||
|
||||
const candidatePageIds = Array.from(
|
||||
new Set(references.map((r) => r.sourcePageId)),
|
||||
);
|
||||
const accessibleSet = viewerUserId
|
||||
? new Set(
|
||||
await this.pagePermissionRepo.filterAccessiblePageIds({
|
||||
pageIds: candidatePageIds,
|
||||
userId: viewerUserId,
|
||||
}),
|
||||
)
|
||||
: new Set<string>();
|
||||
const accessibleSet = new Set(
|
||||
await this.pagePermissionRepo.filterAccessiblePageIds({
|
||||
pageIds: candidatePageIds,
|
||||
userId: viewerUserId,
|
||||
}),
|
||||
);
|
||||
|
||||
return this.lookupWithAccessSet(references, accessibleSet);
|
||||
return this.lookupWithAccessSet(references, accessibleSet, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,6 +242,7 @@ export class TransclusionService {
|
||||
async lookupWithAccessSet(
|
||||
references: Array<{ sourcePageId: string; transclusionId: string }>,
|
||||
accessibleSet: Set<string>,
|
||||
workspaceId: string,
|
||||
): Promise<{ items: TransclusionLookup[] }> {
|
||||
if (references.length === 0) return { items: [] };
|
||||
|
||||
@@ -248,6 +257,7 @@ export class TransclusionService {
|
||||
pageId: references[i].sourcePageId,
|
||||
transclusionId: references[i].transclusionId,
|
||||
})),
|
||||
workspaceId,
|
||||
);
|
||||
const rowKey = (r: { pageId: string; transclusionId: string }) =>
|
||||
`${r.pageId}::${r.transclusionId}`;
|
||||
@@ -256,10 +266,12 @@ export class TransclusionService {
|
||||
const accessiblePageIds = Array.from(
|
||||
new Set(accessiblePending.map((i) => references[i].sourcePageId)),
|
||||
);
|
||||
const pages = await this.pageRepo.findManyByIds(accessiblePageIds);
|
||||
const pages = await this.pageRepo.findManyByIds(accessiblePageIds, {
|
||||
workspaceId,
|
||||
});
|
||||
const pageMeta = new Map<string, Date>();
|
||||
for (const p of pages) {
|
||||
if (!p.deletedAt) pageMeta.set(p.id, p.updatedAt);
|
||||
pageMeta.set(p.id, p.updatedAt);
|
||||
}
|
||||
|
||||
for (const i of pendingIdx) {
|
||||
@@ -306,16 +318,18 @@ export class TransclusionService {
|
||||
sourcePageId: string;
|
||||
transclusionId: string;
|
||||
viewerUserId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<{
|
||||
source: ReferencingPageInfo | null;
|
||||
references: ReferencingPageInfo[];
|
||||
}> {
|
||||
const { sourcePageId, transclusionId, viewerUserId } = opts;
|
||||
const { sourcePageId, transclusionId, viewerUserId, workspaceId } = opts;
|
||||
|
||||
const referencePageIds =
|
||||
await this.pageTransclusionReferencesRepo.findReferencePageIdsByTransclusion(
|
||||
sourcePageId,
|
||||
transclusionId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const candidatePageIds = Array.from(
|
||||
@@ -342,7 +356,7 @@ export class TransclusionService {
|
||||
);
|
||||
const byId = new Map<string, ReferencingPageInfo>();
|
||||
for (const p of rows) {
|
||||
if (!p || p.deletedAt) continue;
|
||||
if (!p || p.deletedAt || p.workspaceId !== workspaceId) continue;
|
||||
const space = (p as Page & { space?: { slug?: string } }).space;
|
||||
byId.set(p.id, {
|
||||
id: p.id,
|
||||
@@ -376,7 +390,7 @@ export class TransclusionService {
|
||||
referencePageId: string,
|
||||
sourcePageId: string,
|
||||
transclusionId: string,
|
||||
viewerUserId: string,
|
||||
user: User,
|
||||
): Promise<{ content: unknown }> {
|
||||
const referencePage = await this.pageRepo.findById(referencePageId);
|
||||
if (!referencePage || referencePage.deletedAt) {
|
||||
@@ -388,16 +402,16 @@ export class TransclusionService {
|
||||
throw new NotFoundException('Source page not found');
|
||||
}
|
||||
|
||||
const accessible = new Set(
|
||||
await this.pagePermissionRepo.filterAccessiblePageIds({
|
||||
pageIds: [referencePageId, sourcePageId],
|
||||
userId: viewerUserId,
|
||||
}),
|
||||
);
|
||||
if (!accessible.has(referencePageId) || !accessible.has(sourcePageId)) {
|
||||
if (
|
||||
referencePage.workspaceId !== user.workspaceId ||
|
||||
sourcePage.workspaceId !== user.workspaceId
|
||||
) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
await this.pageAccessService.validateCanEdit(referencePage, user);
|
||||
await this.pageAccessService.validateCanView(sourcePage, user);
|
||||
|
||||
const transclusion =
|
||||
await this.pageTransclusionsRepo.findByPageAndTransclusion(
|
||||
sourcePageId,
|
||||
@@ -445,7 +459,7 @@ export class TransclusionService {
|
||||
fileSize: old.fileSize,
|
||||
mimeType: old.mimeType,
|
||||
fileExt: old.fileExt,
|
||||
creatorId: viewerUserId,
|
||||
creatorId: user.id,
|
||||
workspaceId: referencePage.workspaceId,
|
||||
pageId: referencePageId,
|
||||
spaceId: referencePage.spaceId,
|
||||
|
||||
@@ -357,6 +357,7 @@ export class ShareService {
|
||||
const { items } = await this.transclusionService.lookupWithAccessSet(
|
||||
references,
|
||||
accessibleSet,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
// Sanitize each item's content for public delivery
|
||||
|
||||
@@ -6,6 +6,9 @@ export async function up(db: Kysely<any>): Promise<void> {
|
||||
.addColumn('id', 'uuid', (col) =>
|
||||
col.primaryKey().defaultTo(sql`gen_uuid_v7()`),
|
||||
)
|
||||
.addColumn('workspace_id', 'uuid', (col) =>
|
||||
col.notNull().references('workspaces.id').onDelete('cascade'),
|
||||
)
|
||||
.addColumn('page_id', 'uuid', (col) =>
|
||||
col.notNull().references('pages.id').onDelete('cascade'),
|
||||
)
|
||||
@@ -23,11 +26,20 @@ export async function up(db: Kysely<any>): Promise<void> {
|
||||
])
|
||||
.execute();
|
||||
|
||||
await db.schema
|
||||
.createIndex('idx_page_transclusions_workspace')
|
||||
.on('page_transclusions')
|
||||
.column('workspace_id')
|
||||
.execute();
|
||||
|
||||
await db.schema
|
||||
.createTable('page_transclusion_references')
|
||||
.addColumn('id', 'uuid', (col) =>
|
||||
col.primaryKey().defaultTo(sql`gen_uuid_v7()`),
|
||||
)
|
||||
.addColumn('workspace_id', 'uuid', (col) =>
|
||||
col.notNull().references('workspaces.id').onDelete('cascade'),
|
||||
)
|
||||
.addColumn('reference_page_id', 'uuid', (col) =>
|
||||
col.notNull().references('pages.id').onDelete('cascade'),
|
||||
)
|
||||
@@ -50,6 +62,12 @@ export async function up(db: Kysely<any>): Promise<void> {
|
||||
.on('page_transclusion_references')
|
||||
.columns(['source_page_id', 'transclusion_id'])
|
||||
.execute();
|
||||
|
||||
await db.schema
|
||||
.createIndex('idx_page_transclusion_references_workspace')
|
||||
.on('page_transclusion_references')
|
||||
.column('workspace_id')
|
||||
.execute();
|
||||
}
|
||||
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
|
||||
@@ -30,12 +30,14 @@ export class PageTransclusionReferencesRepo {
|
||||
async findReferencePageIdsByTransclusion(
|
||||
sourcePageId: string,
|
||||
transclusionId: string,
|
||||
workspaceId: string,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<string[]> {
|
||||
const rows = await dbOrTx(this.db, trx)
|
||||
.selectFrom('pageTransclusionReferences')
|
||||
.select('referencePageId')
|
||||
.distinct()
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.where('sourcePageId', '=', sourcePageId)
|
||||
.where('transclusionId', '=', transclusionId)
|
||||
.execute();
|
||||
|
||||
@@ -39,12 +39,14 @@ export class PageTransclusionsRepo {
|
||||
|
||||
async findManyByPageAndTransclusion(
|
||||
keys: Array<{ pageId: string; transclusionId: string }>,
|
||||
workspaceId: string,
|
||||
trx?: KyselyTransaction,
|
||||
): Promise<PageTransclusion[]> {
|
||||
if (keys.length === 0) return [];
|
||||
return dbOrTx(this.db, trx)
|
||||
.selectFrom('pageTransclusions')
|
||||
.selectAll()
|
||||
.where('workspaceId', '=', workspaceId)
|
||||
.where((eb) =>
|
||||
eb.or(
|
||||
keys.map((k) =>
|
||||
|
||||
@@ -104,16 +104,24 @@ export class PageRepo {
|
||||
pageIds: string[],
|
||||
opts?: {
|
||||
trx?: KyselyTransaction;
|
||||
workspaceId?: string;
|
||||
},
|
||||
): Promise<Page[]> {
|
||||
if (pageIds.length === 0) return [];
|
||||
const db = dbOrTx(this.db, opts?.trx);
|
||||
|
||||
return db
|
||||
let query = db
|
||||
.selectFrom('pages')
|
||||
.select(this.baseFields)
|
||||
.where('id', 'in', pageIds)
|
||||
.execute();
|
||||
.where('id', 'in', pageIds);
|
||||
|
||||
if (opts?.workspaceId) {
|
||||
query = query
|
||||
.where('workspaceId', '=', opts.workspaceId)
|
||||
.where('deletedAt', 'is', null);
|
||||
}
|
||||
|
||||
return query.execute();
|
||||
}
|
||||
|
||||
async updatePage(
|
||||
|
||||
+2
@@ -234,6 +234,7 @@ export interface PageTransclusionReferences {
|
||||
referencePageId: string;
|
||||
id: Generated<string>;
|
||||
sourcePageId: string;
|
||||
workspaceId: string;
|
||||
}
|
||||
|
||||
export interface PageTransclusions {
|
||||
@@ -243,6 +244,7 @@ export interface PageTransclusions {
|
||||
id: Generated<string>;
|
||||
pageId: string;
|
||||
updatedAt: Generated<Timestamp>;
|
||||
workspaceId: string;
|
||||
}
|
||||
|
||||
export interface PageHistory {
|
||||
|
||||
+1
-1
Submodule apps/server/src/ee updated: 35c0f3c4f8...6479522986
Reference in New Issue
Block a user