mirror of
https://github.com/docmost/docmost.git
synced 2026-05-24 03:02:42 +08:00
track link nodes (backlinks)
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
extractMentions,
|
extractMentions,
|
||||||
extractPageMentions,
|
extractPageMentions,
|
||||||
|
extractInternalLinkSlugIds,
|
||||||
} from '../../common/helpers/prosemirror/utils';
|
} from '../../common/helpers/prosemirror/utils';
|
||||||
import { PageHistoryRepo } from '@docmost/db/repos/page/page-history.repo';
|
import { PageHistoryRepo } from '@docmost/db/repos/page/page-history.repo';
|
||||||
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
||||||
@@ -77,12 +78,14 @@ export class HistoryProcessor extends WorkerHost implements OnModuleDestroy {
|
|||||||
|
|
||||||
const mentions = extractMentions(page.content);
|
const mentions = extractMentions(page.content);
|
||||||
const pageMentions = extractPageMentions(mentions);
|
const pageMentions = extractPageMentions(mentions);
|
||||||
|
const internalLinkSlugIds = extractInternalLinkSlugIds(page.content);
|
||||||
|
|
||||||
await this.generalQueue
|
await this.generalQueue
|
||||||
.add(QueueJob.PAGE_BACKLINKS, {
|
.add(QueueJob.PAGE_BACKLINKS, {
|
||||||
pageId,
|
pageId,
|
||||||
workspaceId: page.workspaceId,
|
workspaceId: page.workspaceId,
|
||||||
mentions: pageMentions,
|
mentions: pageMentions,
|
||||||
|
internalLinkSlugIds,
|
||||||
} as IPageBacklinkJob)
|
} as IPageBacklinkJob)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import { validate as isValidUUID } from 'uuid';
|
|||||||
import { Transform } from '@tiptap/pm/transform';
|
import { Transform } from '@tiptap/pm/transform';
|
||||||
import { TiptapTransformer } from '@hocuspocus/transformer';
|
import { TiptapTransformer } from '@hocuspocus/transformer';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
import {
|
||||||
|
INTERNAL_LINK_REGEX,
|
||||||
|
extractPageSlugId,
|
||||||
|
} from '../../../integrations/export/utils';
|
||||||
|
|
||||||
export interface MentionNode {
|
export interface MentionNode {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -64,6 +68,27 @@ export function extractPageMentions(mentionList: MentionNode[]): MentionNode[] {
|
|||||||
return pageMentionList as MentionNode[];
|
return pageMentionList as MentionNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function extractInternalLinkSlugIds(prosemirrorJson: any): string[] {
|
||||||
|
const slugIds: string[] = [];
|
||||||
|
const doc = jsonToNode(prosemirrorJson);
|
||||||
|
|
||||||
|
doc.descendants((node: Node) => {
|
||||||
|
for (const mark of node.marks) {
|
||||||
|
if (mark.type.name === 'link' && mark.attrs.internal && mark.attrs.href) {
|
||||||
|
const match = mark.attrs.href.match(INTERNAL_LINK_REGEX);
|
||||||
|
if (match) {
|
||||||
|
const slugId = extractPageSlugId(match[5]);
|
||||||
|
if (slugId && !slugIds.includes(slugId)) {
|
||||||
|
slugIds.push(slugId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return slugIds;
|
||||||
|
}
|
||||||
|
|
||||||
export function extractUserMentionIdsFromJson(json: any): string[] {
|
export function extractUserMentionIdsFromJson(json: any): string[] {
|
||||||
const userIds: string[] = [];
|
const userIds: string[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface IPageBacklinkJob {
|
|||||||
pageId: string;
|
pageId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
mentions: MentionNode[];
|
mentions: MentionNode[];
|
||||||
|
internalLinkSlugIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAddPageWatchersJob {
|
export interface IAddPageWatchersJob {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function processBacklinks(
|
|||||||
backlinkRepo: BacklinkRepo,
|
backlinkRepo: BacklinkRepo,
|
||||||
data: IPageBacklinkJob,
|
data: IPageBacklinkJob,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { pageId, mentions, workspaceId } = data;
|
const { pageId, mentions, workspaceId, internalLinkSlugIds = [] } = data;
|
||||||
|
|
||||||
await executeTx(db, async (trx) => {
|
await executeTx(db, async (trx) => {
|
||||||
const existingBacklinks = await trx
|
const existingBacklinks = await trx
|
||||||
@@ -20,7 +20,28 @@ export async function processBacklinks(
|
|||||||
.where('sourcePageId', '=', pageId)
|
.where('sourcePageId', '=', pageId)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
if (existingBacklinks.length === 0 && mentions.length === 0) {
|
const mentionTargetPageIds = mentions
|
||||||
|
.filter((mention) => mention.entityId !== pageId)
|
||||||
|
.map((mention) => mention.entityId);
|
||||||
|
|
||||||
|
let resolvedLinkPageIds: string[] = [];
|
||||||
|
if (internalLinkSlugIds.length > 0) {
|
||||||
|
const resolvedPages = await trx
|
||||||
|
.selectFrom('pages')
|
||||||
|
.select('id')
|
||||||
|
.where('slugId', 'in', internalLinkSlugIds)
|
||||||
|
.where('workspaceId', '=', workspaceId)
|
||||||
|
.execute();
|
||||||
|
resolvedLinkPageIds = resolvedPages
|
||||||
|
.map((p) => p.id)
|
||||||
|
.filter((id) => id !== pageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allTargetPageIds = [
|
||||||
|
...new Set([...mentionTargetPageIds, ...resolvedLinkPageIds]),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (existingBacklinks.length === 0 && allTargetPageIds.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,16 +49,12 @@ export async function processBacklinks(
|
|||||||
(backlink) => backlink.targetPageId,
|
(backlink) => backlink.targetPageId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const targetPageIds = mentions
|
|
||||||
.filter((mention) => mention.entityId !== pageId)
|
|
||||||
.map((mention) => mention.entityId);
|
|
||||||
|
|
||||||
let validTargetPages = [];
|
let validTargetPages = [];
|
||||||
if (targetPageIds.length > 0) {
|
if (allTargetPageIds.length > 0) {
|
||||||
validTargetPages = await trx
|
validTargetPages = await trx
|
||||||
.selectFrom('pages')
|
.selectFrom('pages')
|
||||||
.select('id')
|
.select('id')
|
||||||
.where('id', 'in', targetPageIds)
|
.where('id', 'in', allTargetPageIds)
|
||||||
.where('workspaceId', '=', workspaceId)
|
.where('workspaceId', '=', workspaceId)
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user