Compare commits

..

9 Commits

Author SHA1 Message Date
Philipinho 1615e0f4ad v0.22.2 2025-08-01 16:15:02 -07:00
Philip Okugbe 1cb2535de3 fix trash in search (#1439)
- delete share if page is trashed
2025-08-02 00:14:00 +01:00
Philipinho 83bc273cb0 cleanup 2025-08-01 07:05:25 -07:00
Philipinho c7beaa3742 v0.22.1 2025-08-01 06:54:28 -07:00
Philipinho 4a228e5a51 fix comment replies 2025-08-01 06:51:56 -07:00
Philipinho edff375476 sync 2025-08-01 02:54:11 -07:00
Philipinho 95016b2bfc sync 2025-08-01 02:51:55 -07:00
Philipinho ca83712364 cleanup 2025-08-01 02:26:14 -07:00
Philip Okugbe 39550fe906 fix: duplicate page position bug (#1431) 2025-07-30 18:07:06 +01:00
12 changed files with 76 additions and 69 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "client",
"private": true,
"version": "0.22.0",
"version": "0.22.2",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
@@ -1,10 +1,10 @@
import { Group, Text } from "@mantine/core";
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
import React from "react";
import { User } from "server/dist/database/types/entity.types";
import { IUser } from '@/features/user/types/user.types.ts';
interface UserInfoProps {
user: User;
user: Partial<IUser>;
size?: string;
}
export function UserInfo({ user, size }: UserInfoProps) {
@@ -41,6 +41,7 @@ function CommentListWithTabs() {
const spaceRules = space?.membership?.permissions;
const spaceAbility = useSpaceAbility(spaceRules);
const canComment: boolean = spaceAbility.can(
SpaceCaslAction.Manage,
SpaceCaslSubject.Page
@@ -179,6 +180,17 @@ function CommentListWithTabs() {
userSpaceRole={space?.membership?.role}
/>
</div>
{canComment && (
<>
<Divider my={4} />
<CommentEditorWithActions
commentId={comment.id}
onSave={handleAddReply}
isLoading={isLoading}
/>
</>
)}
</Paper>
))}
</div>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "server",
"version": "0.22.0",
"version": "0.22.2",
"description": "",
"author": "",
"private": true,
@@ -43,7 +43,7 @@ export class CommentController {
@AuthWorkspace() workspace: Workspace,
) {
const page = await this.pageRepo.findById(createCommentDto.pageId);
if (!page) {
if (!page || page.deletedAt) {
throw new NotFoundException('Page not found');
}
@@ -90,7 +90,10 @@ export class CommentController {
throw new NotFoundException('Comment not found');
}
const ability = await this.spaceAbility.createForUser(user, comment.spaceId);
const ability = await this.spaceAbility.createForUser(
user,
comment.spaceId,
);
if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
throw new ForbiddenException();
}
@@ -261,35 +261,7 @@ export class PageService {
if (isDuplicateInSameSpace) {
// For duplicate in same space, position right after the original page
let siblingQuery = this.db
.selectFrom('pages')
.select(['position'])
.where('spaceId', '=', rootPage.spaceId)
.where('position', '>', rootPage.position);
if (rootPage.parentPageId) {
siblingQuery = siblingQuery.where(
'parentPageId',
'=',
rootPage.parentPageId,
);
} else {
siblingQuery = siblingQuery.where('parentPageId', 'is', null);
}
const nextSibling = await siblingQuery
.orderBy('position', 'asc')
.limit(1)
.executeTakeFirst();
if (nextSibling) {
nextPosition = generateJitteredKeyBetween(
rootPage.position,
nextSibling.position,
);
} else {
nextPosition = generateJitteredKeyBetween(rootPage.position, null);
}
nextPosition = generateJitteredKeyBetween(rootPage.position, null);
} else {
// For copy to different space, position at the end
nextPosition = await this.nextPagePosition(spaceId);
@@ -434,25 +406,35 @@ export class PageService {
attachment.id,
newAttachmentId,
);
await this.storageService.copy(attachment.filePath, newPathFile);
await this.db
.insertInto('attachments')
.values({
id: newAttachmentId,
type: attachment.type,
filePath: newPathFile,
fileName: attachment.fileName,
fileSize: attachment.fileSize,
mimeType: attachment.mimeType,
fileExt: attachment.fileExt,
creatorId: attachment.creatorId,
workspaceId: attachment.workspaceId,
pageId: newPageId,
spaceId: spaceId,
})
.execute();
try {
await this.storageService.copy(attachment.filePath, newPathFile);
await this.db
.insertInto('attachments')
.values({
id: newAttachmentId,
type: attachment.type,
filePath: newPathFile,
fileName: attachment.fileName,
fileSize: attachment.fileSize,
mimeType: attachment.mimeType,
fileExt: attachment.fileExt,
creatorId: attachment.creatorId,
workspaceId: attachment.workspaceId,
pageId: newPageId,
spaceId: spaceId,
})
.execute();
} catch (err) {
this.logger.error(
`Duplicate page: failed to copy attachment ${attachment.id}`,
err,
);
// Continue with other attachments even if one fails
}
} catch (err) {
this.logger.log(err);
this.logger.error(err);
}
}
}
@@ -19,7 +19,7 @@ export class TrashCleanupService {
@Interval('trash-cleanup', 24 * 60 * 60 * 1000) // every 24 hours
async cleanupOldTrash() {
try {
this.logger.log('Starting trash cleanup job');
this.logger.debug('Starting trash cleanup job');
const retentionDate = new Date();
retentionDate.setDate(retentionDate.getDate() - this.RETENTION_DAYS);
@@ -59,6 +59,7 @@ export class SearchService {
.$if(Boolean(searchParams.creatorId), (qb) =>
qb.where('creatorId', '=', searchParams.creatorId),
)
.where('deletedAt', 'is', null)
.orderBy('rank', 'desc')
.limit(searchParams.limit | 20)
.offset(searchParams.offset || 0);
@@ -191,6 +192,7 @@ export class SearchService {
sql`LOWER(f_unaccent(${`%${query}%`}))`,
),
)
.where('deletedAt', 'is', null)
.where('workspaceId', '=', workspaceId)
.limit(limit);
+6 -4
View File
@@ -108,12 +108,12 @@ export class ShareService {
includeCreator: true,
});
page.content = await this.updatePublicAttachments(page);
if (!page) {
if (!page || page.deletedAt) {
throw new NotFoundException('Shared page not found');
}
page.content = await this.updatePublicAttachments(page);
return { page, share };
}
@@ -132,6 +132,7 @@ export class ShareService {
sql`0`.as('level'),
])
.where(isValidUUID(pageId) ? 'id' : 'slugId', '=', pageId)
.where('deletedAt', 'is', null)
.unionAll((union) =>
union
.selectFrom('pages as p')
@@ -144,7 +145,8 @@ export class ShareService {
// Increase the level by 1 for each ancestor.
sql`ph.level + 1`.as('level'),
])
.innerJoin('page_hierarchy as ph', 'ph.parentPageId', 'p.id'),
.innerJoin('page_hierarchy as ph', 'ph.parentPageId', 'p.id')
.where('p.deletedAt', 'is', null),
),
)
.selectFrom('page_hierarchy')
@@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { InjectKysely } from 'nestjs-kysely';
import { KyselyDB, KyselyTransaction } from '../../types/kysely.types';
import { dbOrTx } from '../../utils';
import { dbOrTx, executeTx } from '../../utils';
import {
InsertablePage,
Page,
@@ -183,14 +183,20 @@ export class PageRepo {
const pageIds = descendants.map((d) => d.id);
await this.db
.updateTable('pages')
.set({
deletedById: deletedById,
deletedAt: currentDate,
})
.where('id', 'in', pageIds)
.execute();
if (pageIds.length > 0) {
await executeTx(this.db, async (trx) => {
await trx
.updateTable('pages')
.set({
deletedById: deletedById,
deletedAt: currentDate,
})
.where('id', 'in', pageIds)
.execute();
await trx.deleteFrom('shares').where('pageId', 'in', pageIds).execute();
});
}
}
async restorePage(pageId: string): Promise<void> {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "docmost",
"homepage": "https://docmost.com",
"version": "0.22.0",
"version": "0.22.2",
"private": true,
"scripts": {
"build": "nx run-many -t build",