feat(server): make permission a standalone module (#7880)

This commit is contained in:
forehalo
2024-08-15 10:01:52 +00:00
parent ba8958f39b
commit 624f3514fc
30 changed files with 123 additions and 116 deletions

View File

@@ -11,10 +11,9 @@ import {
DocNotFound,
metrics,
OnEvent,
WorkspaceNotFound,
} from '../../fundamentals';
import { PermissionService } from '../permission';
import { QuotaService } from '../quota';
import { Permission } from '../workspaces/types';
import { isEmptyBuffer } from './manager';
@Injectable()
@@ -23,7 +22,8 @@ export class DocHistoryManager {
constructor(
private readonly config: Config,
private readonly db: PrismaClient,
private readonly quota: QuotaService
private readonly quota: QuotaService,
private readonly permission: PermissionService
) {}
@OnEvent('workspace.deleted')
@@ -235,21 +235,8 @@ export class DocHistoryManager {
}
async getExpiredDateFromNow(workspaceId: string) {
const permission = await this.db.workspaceUserPermission.findFirst({
select: {
userId: true,
},
where: {
workspaceId,
type: Permission.Owner,
},
});
if (!permission) {
throw new WorkspaceNotFound({ workspaceId });
}
const quota = await this.quota.getUserQuota(permission.userId);
const owner = await this.permission.getWorkspaceOwner(workspaceId);
const quota = await this.quota.getUserQuota(owner.id);
return quota.feature.historyPeriodFromNow;
}

View File

@@ -2,12 +2,13 @@ import './config';
import { Module } from '@nestjs/common';
import { PermissionModule } from '../permission';
import { QuotaModule } from '../quota';
import { DocHistoryManager } from './history';
import { DocManager } from './manager';
@Module({
imports: [QuotaModule],
imports: [QuotaModule, PermissionModule],
providers: [DocManager, DocHistoryManager],
exports: [DocManager, DocHistoryManager],
})

View File

@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { PermissionService } from './service';
@Module({
providers: [PermissionService],
exports: [PermissionService],
})
export class PermissionModule {}
export { PermissionService } from './service';
export { Permission, PublicPageMode } from './types';

View File

@@ -2,13 +2,12 @@ import { Injectable } from '@nestjs/common';
import type { Prisma } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import { DocAccessDenied, WorkspaceAccessDenied } from '../../fundamentals';
import { Permission } from './types';
export enum PublicPageMode {
Page,
Edgeless,
}
import {
DocAccessDenied,
WorkspaceAccessDenied,
WorkspaceOwnerNotFound,
} from '../../fundamentals';
import { Permission, PublicPageMode } from './types';
@Injectable()
export class PermissionService {
@@ -59,7 +58,7 @@ export class PermissionService {
}
async getWorkspaceOwner(workspaceId: string) {
return this.prisma.workspaceUserPermission.findFirstOrThrow({
const owner = await this.prisma.workspaceUserPermission.findFirst({
where: {
workspaceId,
type: Permission.Owner,
@@ -68,6 +67,12 @@ export class PermissionService {
user: true,
},
});
if (!owner) {
throw new WorkspaceOwnerNotFound({ workspaceId });
}
return owner.user;
}
async tryGetWorkspaceOwner(workspaceId: string) {
@@ -195,6 +200,17 @@ export class PermissionService {
return false;
}
async allowUrlPreview(ws: string) {
const count = await this.prisma.workspace.count({
where: {
id: ws,
public: true,
},
});
return count > 0;
}
async grant(
ws: string,
user: string,

View File

@@ -0,0 +1,11 @@
export enum Permission {
Read = 0,
Write = 1,
Admin = 10,
Owner = 99,
}
export enum PublicPageMode {
Page,
Edgeless,
}

View File

@@ -1,8 +1,8 @@
import { Module } from '@nestjs/common';
import { FeatureModule } from '../features';
import { PermissionModule } from '../permission';
import { StorageModule } from '../storage';
import { PermissionService } from '../workspaces/permission';
import { QuotaManagementResolver } from './resolver';
import { QuotaService } from './service';
import { QuotaManagementService } from './storage';
@@ -14,13 +14,8 @@ import { QuotaManagementService } from './storage';
* - quota statistics
*/
@Module({
imports: [FeatureModule, StorageModule],
providers: [
PermissionService,
QuotaService,
QuotaManagementResolver,
QuotaManagementService,
],
imports: [FeatureModule, StorageModule, PermissionModule],
providers: [QuotaService, QuotaManagementResolver, QuotaManagementService],
exports: [QuotaService, QuotaManagementService],
})
export class QuotaModule {}

View File

@@ -1,9 +1,8 @@
import { Injectable, Logger } from '@nestjs/common';
import { WorkspaceOwnerNotFound } from '../../fundamentals';
import { FeatureService, FeatureType } from '../features';
import { PermissionService } from '../permission';
import { WorkspaceBlobStorage } from '../storage';
import { PermissionService } from '../workspaces/permission';
import { OneGB } from './constant';
import { QuotaService } from './service';
import { formatSize, QuotaQueryType } from './types';
@@ -113,9 +112,7 @@ export class QuotaManagementService {
// get workspace's owner quota and total size of used
// quota was apply to owner's account
async getWorkspaceUsage(workspaceId: string): Promise<QuotaBusinessType> {
const { user: owner } =
await this.permissions.getWorkspaceOwner(workspaceId);
if (!owner) throw new WorkspaceOwnerNotFound({ workspaceId });
const owner = await this.permissions.getWorkspaceOwner(workspaceId);
const {
feature: {
name,

View File

@@ -23,9 +23,8 @@ import {
} from '../../../fundamentals';
import { Auth, CurrentUser } from '../../auth';
import { DocManager } from '../../doc';
import { Permission, PermissionService } from '../../permission';
import { DocID } from '../../utils/doc';
import { PermissionService } from '../../workspaces/permission';
import { Permission } from '../../workspaces/types';
const SubscribeMessage = (event: string) =>
applyDecorators(

View File

@@ -1,11 +1,11 @@
import { Module } from '@nestjs/common';
import { DocModule } from '../../doc';
import { PermissionService } from '../../workspaces/permission';
import { PermissionModule } from '../../permission';
import { EventsGateway } from './events.gateway';
@Module({
imports: [DocModule],
providers: [EventsGateway, PermissionService],
imports: [DocModule, PermissionModule],
providers: [EventsGateway],
})
export class EventsModule {}

View File

@@ -13,10 +13,9 @@ import {
} from '../../fundamentals';
import { CurrentUser, Public } from '../auth';
import { DocHistoryManager, DocManager } from '../doc';
import { Permission, PermissionService, PublicPageMode } from '../permission';
import { WorkspaceBlobStorage } from '../storage';
import { DocID } from '../utils/doc';
import { PermissionService, PublicPageMode } from './permission';
import { Permission } from './types';
@Controller('/api/workspaces')
export class WorkspacesController {

View File

@@ -2,12 +2,12 @@ import { Module } from '@nestjs/common';
import { DocModule } from '../doc';
import { FeatureModule } from '../features';
import { PermissionModule } from '../permission';
import { QuotaModule } from '../quota';
import { StorageModule } from '../storage';
import { UserModule } from '../user';
import { WorkspacesController } from './controller';
import { WorkspaceManagementResolver } from './management';
import { PermissionService } from './permission';
import {
DocHistoryResolver,
PagePermissionResolver,
@@ -16,17 +16,22 @@ import {
} from './resolvers';
@Module({
imports: [DocModule, FeatureModule, QuotaModule, StorageModule, UserModule],
imports: [
DocModule,
FeatureModule,
QuotaModule,
StorageModule,
UserModule,
PermissionModule,
],
controllers: [WorkspacesController],
providers: [
WorkspaceResolver,
WorkspaceManagementResolver,
PermissionService,
PagePermissionResolver,
DocHistoryResolver,
WorkspaceBlobResolver,
],
exports: [PermissionService],
})
export class WorkspaceModule {}

View File

@@ -12,7 +12,7 @@ import { ActionForbidden } from '../../fundamentals';
import { CurrentUser } from '../auth';
import { Admin } from '../common';
import { FeatureManagementService, FeatureType } from '../features';
import { PermissionService } from './permission';
import { PermissionService } from '../permission';
import { WorkspaceType } from './types';
@Resolver(() => WorkspaceType)
@@ -61,7 +61,7 @@ export class WorkspaceManagementResolver {
const owner = await this.permission.getWorkspaceOwner(workspaceId);
const availableFeatures = await this.availableFeatures(user);
if (owner.user.id !== user.id || !availableFeatures.includes(feature)) {
if (owner.id !== user.id || !availableFeatures.includes(feature)) {
throw new ActionForbidden();
}

View File

@@ -19,10 +19,10 @@ import {
PreventCache,
} from '../../../fundamentals';
import { CurrentUser } from '../../auth';
import { Permission, PermissionService } from '../../permission';
import { QuotaManagementService } from '../../quota';
import { WorkspaceBlobStorage } from '../../storage';
import { PermissionService } from '../permission';
import { Permission, WorkspaceBlobSizes, WorkspaceType } from '../types';
import { WorkspaceBlobSizes, WorkspaceType } from '../types';
@UseGuards(CloudThrottlerGuard)
@Resolver(() => WorkspaceType)

View File

@@ -13,9 +13,9 @@ import type { SnapshotHistory } from '@prisma/client';
import { CurrentUser } from '../../auth';
import { DocHistoryManager } from '../../doc';
import { Permission, PermissionService } from '../../permission';
import { DocID } from '../../utils/doc';
import { PermissionService } from '../permission';
import { Permission, WorkspaceType } from '../types';
import { WorkspaceType } from '../types';
@ObjectType()
class DocHistoryType implements Partial<SnapshotHistory> {

View File

@@ -17,9 +17,13 @@ import {
PageIsNotPublic,
} from '../../../fundamentals';
import { CurrentUser } from '../../auth';
import {
Permission,
PermissionService,
PublicPageMode,
} from '../../permission';
import { DocID } from '../../utils/doc';
import { PermissionService, PublicPageMode } from '../permission';
import { Permission, WorkspaceType } from '../types';
import { WorkspaceType } from '../types';
registerEnumType(PublicPageMode, {
name: 'PublicPageMode',

View File

@@ -26,17 +26,15 @@ import {
UserNotFound,
WorkspaceAccessDenied,
WorkspaceNotFound,
WorkspaceOwnerNotFound,
} from '../../../fundamentals';
import { CurrentUser, Public } from '../../auth';
import { Permission, PermissionService } from '../../permission';
import { QuotaManagementService, QuotaQueryType } from '../../quota';
import { WorkspaceBlobStorage } from '../../storage';
import { UserService, UserType } from '../../user';
import { PermissionService } from '../permission';
import {
InvitationType,
InviteUserType,
Permission,
UpdateWorkspaceInput,
WorkspaceType,
} from '../types';
@@ -101,9 +99,7 @@ export class WorkspaceResolver {
complexity: 2,
})
async owner(@Parent() workspace: WorkspaceType) {
const data = await this.permissions.getWorkspaceOwner(workspace.id);
return data.user;
return this.permissions.getWorkspaceOwner(workspace.id);
}
@ResolveField(() => [InviteUserType], {
@@ -449,7 +445,7 @@ export class WorkspaceResolver {
avatar: avatar || defaultWorkspaceAvatar,
id: workspaceId,
},
user: owner.user,
user: owner,
invitee: invitee.user,
};
}
@@ -507,12 +503,8 @@ export class WorkspaceResolver {
const owner = await this.permissions.getWorkspaceOwner(workspaceId);
if (!owner.user) {
throw new WorkspaceOwnerNotFound({ workspaceId: workspaceId });
}
if (sendLeaveMail) {
await this.mailer.sendLeaveWorkspaceEmail(owner.user.email, {
await this.mailer.sendLeaveWorkspaceEmail(owner.email, {
workspaceName,
inviteeName: user.name,
});

View File

@@ -11,15 +11,9 @@ import {
import type { Workspace } from '@prisma/client';
import { SafeIntResolver } from 'graphql-scalars';
import { Permission } from '../permission';
import { UserType } from '../user/types';
export enum Permission {
Read = 0,
Write = 1,
Admin = 10,
Owner = 99,
}
registerEnumType(Permission, {
name: 'Permission',
description: 'User permission in workspace',