mirror of
https://github.com/docmost/docmost.git
synced 2026-07-22 18:12:47 +10:00
bug fixes (#2250)
* util * fix page position collation * support fixed toolbar in templates editor * date localization * fix clipped emoji in templates editor * fix page updated time object * fix flickers * fix: remove redundant breadcrumb from destination modal
This commit is contained in:
@@ -165,6 +165,21 @@ export class PersistenceExtension implements Extension {
|
||||
}
|
||||
|
||||
if (page) {
|
||||
document.broadcastStateless(
|
||||
JSON.stringify({
|
||||
type: 'page.updated',
|
||||
updatedAt: new Date().toISOString(),
|
||||
lastUpdatedById: context?.user?.id,
|
||||
lastUpdatedBy: context?.user
|
||||
? {
|
||||
id: context.user?.id,
|
||||
name: context.user?.name,
|
||||
avatarUrl: context.user?.avatarUrl,
|
||||
}
|
||||
: undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
await this.syncTransclusion(pageId, page.workspaceId, tiptapJson);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@ export enum UserRole {
|
||||
MEMBER = 'member',
|
||||
}
|
||||
|
||||
export enum InviteUserRole {
|
||||
ADMIN = 'admin', // can have owner permissions but cannot delete workspace
|
||||
MEMBER = 'member',
|
||||
}
|
||||
|
||||
export enum SpaceRole {
|
||||
ADMIN = 'admin', // can manage space settings, members, and delete space
|
||||
WRITER = 'writer', // can read and write pages in space
|
||||
|
||||
@@ -310,6 +310,7 @@ export class PageService {
|
||||
expression: 'position',
|
||||
direction: 'asc',
|
||||
orderModifier: (ob) => ob.collate('C').asc(),
|
||||
cursorExpression: sql`position collate "C"`,
|
||||
},
|
||||
{ expression: 'id', direction: 'asc' },
|
||||
],
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
MaxLength,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
import { UserRole } from '../../../common/helpers/types/permission';
|
||||
import { InviteUserRole } from '../../../common/helpers/types/permission';
|
||||
import { NoUrls } from '../../../common/validators/no-urls.validator';
|
||||
|
||||
export class InviteUserDto {
|
||||
@@ -32,7 +32,7 @@ export class InviteUserDto {
|
||||
@IsUUID('all', { each: true })
|
||||
groupIds: string[];
|
||||
|
||||
@IsEnum(UserRole)
|
||||
@IsEnum(InviteUserRole)
|
||||
role: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
ForbiddenException,
|
||||
Inject,
|
||||
Injectable,
|
||||
Logger,
|
||||
@@ -40,6 +41,7 @@ import {
|
||||
AUDIT_SERVICE,
|
||||
IAuditService,
|
||||
} from '../../../integrations/audit/audit.service';
|
||||
import { isAdminActingOnOwner } from '../workspace.util';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceInvitationService {
|
||||
@@ -119,6 +121,10 @@ export class WorkspaceInvitationService {
|
||||
): Promise<void> {
|
||||
const { emails, role, groupIds } = inviteUserDto;
|
||||
|
||||
if (isAdminActingOnOwner(authUser.role, role)) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
let invites: WorkspaceInvitation[] = [];
|
||||
|
||||
try {
|
||||
|
||||
@@ -30,6 +30,7 @@ import { DomainService } from '../../../integrations/environment/domain.service'
|
||||
import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
||||
import { addDays } from 'date-fns';
|
||||
import { DISALLOWED_HOSTNAMES, WorkspaceStatus } from '../workspace.constants';
|
||||
import { isAdminActingOnOwner } from '../workspace.util';
|
||||
import { v4 } from 'uuid';
|
||||
import { InjectQueue } from '@nestjs/bullmq';
|
||||
import { QueueJob, QueueName } from '../../../integrations/queue/constants';
|
||||
@@ -590,8 +591,8 @@ export class WorkspaceService {
|
||||
|
||||
// prevent ADMIN from managing OWNER role
|
||||
if (
|
||||
(authUser.role === UserRole.ADMIN && newRole === UserRole.OWNER) ||
|
||||
(authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER)
|
||||
isAdminActingOnOwner(authUser.role, newRole) ||
|
||||
isAdminActingOnOwner(authUser.role, user.role)
|
||||
) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
@@ -695,7 +696,7 @@ export class WorkspaceService {
|
||||
throw new BadRequestException('You cannot deactivate yourself');
|
||||
}
|
||||
|
||||
if (authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER) {
|
||||
if (isAdminActingOnOwner(authUser.role, user.role)) {
|
||||
throw new BadRequestException(
|
||||
'You cannot deactivate a user with owner role',
|
||||
);
|
||||
@@ -753,7 +754,7 @@ export class WorkspaceService {
|
||||
throw new BadRequestException('User is not deactivated');
|
||||
}
|
||||
|
||||
if (authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER) {
|
||||
if (isAdminActingOnOwner(authUser.role, user.role)) {
|
||||
throw new BadRequestException(
|
||||
'You cannot activate a user with owner role',
|
||||
);
|
||||
@@ -805,7 +806,7 @@ export class WorkspaceService {
|
||||
throw new BadRequestException('You cannot delete yourself');
|
||||
}
|
||||
|
||||
if (authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER) {
|
||||
if (isAdminActingOnOwner(authUser.role, user.role)) {
|
||||
throw new BadRequestException('You cannot delete a user with owner role');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { UserRole } from '../../common/helpers/types/permission';
|
||||
|
||||
export function isAdminActingOnOwner(
|
||||
authUserRole: string,
|
||||
targetRole: string,
|
||||
): boolean {
|
||||
return authUserRole === UserRole.ADMIN && targetRole === UserRole.OWNER;
|
||||
}
|
||||
@@ -14,12 +14,14 @@ type SortField<DB, TB extends keyof DB, O> =
|
||||
| (StringReference<DB, TB> & `${string}.${keyof O & string}`);
|
||||
direction: OrderByDirection;
|
||||
orderModifier?: OrderByModifiers;
|
||||
cursorExpression?: ReferenceExpression<DB, TB>;
|
||||
key?: keyof O & string;
|
||||
}
|
||||
| {
|
||||
expression: ReferenceExpression<DB, TB>;
|
||||
direction: OrderByDirection;
|
||||
orderModifier?: OrderByModifiers;
|
||||
cursorExpression?: ReferenceExpression<DB, TB>;
|
||||
key: keyof O & string;
|
||||
};
|
||||
|
||||
@@ -202,11 +204,12 @@ export async function executeWithCursorPagination<
|
||||
|
||||
const comparison = field.direction === defaultDirection ? '>' : '<';
|
||||
const value = cursor[field.key as keyof typeof cursor];
|
||||
const compareExpr = field.cursorExpression ?? field.expression;
|
||||
|
||||
const conditions = [eb(field.expression, comparison, value)];
|
||||
const conditions = [eb(compareExpr, comparison, value)];
|
||||
|
||||
if (expression) {
|
||||
conditions.push(and([eb(field.expression, '=', value), expression]));
|
||||
conditions.push(and([eb(compareExpr, '=', value), expression]));
|
||||
}
|
||||
|
||||
expression = or(conditions);
|
||||
|
||||
Reference in New Issue
Block a user