Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from 'nestjs-i18n';
import * as path from 'path';
import { NamespaceResourcesModule } from 'omniboxd/namespace-resources/namespace-resources.module';
import { NamespaceTasksModule } from 'omniboxd/namespace-tasks/namespace-tasks.module';
import { SnakeCaseInterceptor } from 'omniboxd/interceptor/snake-case';
import { NamespacesModule } from 'omniboxd/namespaces/namespaces.module';
import { PermissionsModule } from 'omniboxd/permissions/permissions.module';
Expand Down Expand Up @@ -75,6 +76,7 @@ import { OpenAPIModule } from 'omniboxd/open-api/open-api.module';
import { UserUsernameNotNull1763533615604 } from 'omniboxd/migrations/1763533615604-user-username-not-null';
import { AddMetadataToUserBindings1762847685000 } from 'omniboxd/migrations/1762847685000-add-metadata-to-user-bindings';
import { AddEnqueuedToTasks1765348624000 } from 'omniboxd/migrations/1765348624000-add-enqueued-to-tasks';
import { AddResourceIdToTasks1765443191000 } from 'omniboxd/migrations/1765443191000-add-resource-id-to-tasks';
import { KafkaModule } from 'omniboxd/kafka/kafka.module';

@Module({})
Expand Down Expand Up @@ -137,6 +139,7 @@ export class AppModule implements NestModule {
APIKeyModule,
NamespacesModule,
NamespaceResourcesModule,
NamespaceTasksModule,
ResourcesModule,
TasksModule,
WizardModule,
Expand Down Expand Up @@ -206,6 +209,7 @@ export class AppModule implements NestModule {
UserUsernameNotNull1763533615604,
AddMetadataToUserBindings1762847685000,
AddEnqueuedToTasks1765348624000,
AddResourceIdToTasks1765443191000,
...extraMigrations,
],
migrationsRun: true,
Expand Down
32 changes: 32 additions & 0 deletions src/migrations/1765443191000-add-resource-id-to-tasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
MigrationInterface,
QueryRunner,
TableColumn,
TableForeignKey,
} from 'typeorm';

export class AddResourceIdToTasks1765443191000 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
'tasks',
new TableColumn({
name: 'resource_id',
type: 'character varying',
isNullable: true,
}),
);

await queryRunner.createForeignKey(
'tasks',
new TableForeignKey({
columnNames: ['resource_id'],
referencedTableName: 'resources',
referencedColumnNames: ['id'],
}),
);
}

public down(): Promise<void> {
throw new Error('Not supported.');
}
}
6 changes: 5 additions & 1 deletion src/namespace-resources/namespace-resources.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,11 @@ export class NamespaceResourcesController {
@Param('namespaceId') namespaceId: string,
@Param('resourceId') resourceId: string,
) {
await this.namespaceResourcesService.restore(userId, resourceId);
await this.namespaceResourcesService.restore(
userId,
namespaceId,
resourceId,
);
const resource = await this.namespaceResourcesService.getResource({
namespaceId,
resourceId,
Expand Down
29 changes: 9 additions & 20 deletions src/namespace-resources/namespace-resources.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { Injectable, HttpStatus } from '@nestjs/common';
import { AppException } from 'omniboxd/common/exceptions/app.exception';
import { I18nService } from 'nestjs-i18n';
import { S3Service } from 'omniboxd/s3/s3.service';
import { WizardTaskService } from 'omniboxd/tasks/wizard-task.service';
import { PermissionsService } from 'omniboxd/permissions/permissions.service';
import { PrivateSearchResourceDto } from 'omniboxd/wizard/dto/agent-request.dto';
import {
Expand All @@ -44,7 +43,6 @@ import {
} from './dto/file-info.dto';
import { getOriginalFileName } from 'omniboxd/utils/encode-filename';

const TASK_PRIORITY = 5;
@Injectable()
export class NamespaceResourcesService {
constructor(
Expand All @@ -57,7 +55,6 @@ export class NamespaceResourcesService {
private readonly s3Service: S3Service,
private readonly permissionsService: PermissionsService,
private readonly resourceAttachmentsService: ResourceAttachmentsService,
private readonly wizardTaskService: WizardTaskService,
private readonly resourcesService: ResourcesService,
private readonly filesService: FilesService,
private readonly i18n: I18nService,
Expand Down Expand Up @@ -778,18 +775,15 @@ export class NamespaceResourcesService {
HttpStatus.BAD_REQUEST,
);
}
await transaction(this.dataSource.manager, async (tx) => {
const manager = tx.entityManager;
await manager.softDelete(Resource, id);
await this.wizardTaskService.emitDeleteIndexTask(userId, resource, tx);
});
await this.resourcesService.deleteResource(userId, namespaceId, id);
}

async restore(userId: string, id: string) {
async restore(userId: string, namespaceId: string, resourceId: string) {
const resource = await this.resourceRepository.findOne({
withDeleted: true,
where: {
id,
namespaceId,
id: resourceId,
},
});
if (!resource) {
Expand All @@ -808,16 +802,11 @@ export class NamespaceResourcesService {
HttpStatus.BAD_REQUEST,
);
}
await transaction(this.dataSource.manager, async (tx) => {
const manager = tx.entityManager;
await manager.restore(Resource, id);
await this.wizardTaskService.emitUpsertIndexTask(
TASK_PRIORITY,
userId,
resource,
tx,
);
});
await this.resourcesService.restoreResource(
userId,
namespaceId,
resourceId,
);
}

s3Path(resourceId: string) {
Expand Down
22 changes: 22 additions & 0 deletions src/namespace-tasks/namespace-tasks.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Controller, Get, Param, Query } from '@nestjs/common';
import { NamespaceTasksService } from './namespace-tasks.service';

@Controller('api/v1/namespaces/:namespaceId/tasks')
export class NamespaceTasksController {
constructor(private readonly namespaceTasksService: NamespaceTasksService) {}

@Get()
async listTasks(
@Param('namespaceId') namespaceId: string,
@Query('offset') offset: number = 0,
@Query('limit') limit: number = 10,
@Query('userId') userId?: string,
) {
return await this.namespaceTasksService.list(
namespaceId,
offset,
limit,
userId,
);
}
}
12 changes: 12 additions & 0 deletions src/namespace-tasks/namespace-tasks.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { NamespaceTasksController } from 'omniboxd/namespace-tasks/namespace-tasks.controller';
import { NamespaceTasksService } from 'omniboxd/namespace-tasks/namespace-tasks.service';
import { TasksModule } from 'omniboxd/tasks/tasks.module';
import { ResourcesModule } from 'omniboxd/resources/resources.module';

@Module({
providers: [NamespaceTasksService],
controllers: [NamespaceTasksController],
imports: [TasksModule, ResourcesModule],
})
export class NamespaceTasksModule {}
58 changes: 58 additions & 0 deletions src/namespace-tasks/namespace-tasks.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Injectable } from '@nestjs/common';
import { ResourcesService } from 'omniboxd/resources/resources.service';
import { TaskMetaDto } from 'omniboxd/tasks/dto/task.dto';
import { TasksService } from 'omniboxd/tasks/tasks.service';

@Injectable()
export class NamespaceTasksService {
constructor(
private readonly tasksService: TasksService,
private readonly resourcesService: ResourcesService,
) {}

async list(
namespaceId: string,
offset: number,
limit: number,
userId?: string,
): Promise<{ tasks: TaskMetaDto[]; total: number }> {
const { tasks, total } = await this.tasksService.list(
namespaceId,
offset,
limit,
userId,
);

const resourceIds = [
...new Set(
tasks
.map((task) => task.resource_id)
.filter((id): id is string => !!id),
),
];

const resources = await this.resourcesService.batchGetResourceMeta(
namespaceId,
resourceIds,
);

for (const task of tasks) {
const resource = task.resource_id
? resources.get(task.resource_id)
: undefined;
if (resource) {
task.canCancel =
(task.status === 'pending' || task.status === 'running') &&
task.function !== 'delete_index';
task.canRerun = task.status === 'canceled';
task.canRedirect = true;
} else {
task.canCancel = false;
task.canRerun = false;
task.canRedirect = false;
}
}

return { tasks, total };
}
}
22 changes: 12 additions & 10 deletions src/namespaces/namespaces.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,12 @@ export class NamespacesService {
namespaceMember: NamespaceMember | null,
tx: Transaction,
): Promise<string> {
const entityManager = tx.entityManager;

if (namespaceMember) {
await this.resourcesService.restoreResource(
userId,
namespaceId,
namespaceMember.rootResourceId,
entityManager,
tx,
);
return namespaceMember.rootResourceId;
}
Expand Down Expand Up @@ -411,32 +410,35 @@ export class NamespacesService {
}

async deleteMember(namespaceId: string, userId: string) {
await this.dataSource.transaction(async (manager) => {
const member = await manager.findOne(NamespaceMember, {
await transaction(this.dataSource.manager, async (tx) => {
const entityManager = tx.entityManager;

const member = await entityManager.findOne(NamespaceMember, {
where: { namespaceId, userId },
});
if (!member) {
return;
}
// Delete private root
await this.resourcesService.deleteResource(
userId,
namespaceId,
member.rootResourceId,
manager,
tx,
);
// Clear user permissions
await manager.softDelete(UserPermission, {
await entityManager.softDelete(UserPermission, {
namespaceId,
userId,
});
// Remove user from all groups
await manager.softDelete(GroupUser, {
await entityManager.softDelete(GroupUser, {
namespaceId,
userId,
});
// Delete namespace member record
await manager.softDelete(NamespaceMember, { id: member.id });
const hasOwner = await this.hasOwner(namespaceId, manager);
await entityManager.softDelete(NamespaceMember, { id: member.id });
const hasOwner = await this.hasOwner(namespaceId, entityManager);
if (!hasOwner) {
throw new AppException(
this.i18n.t('namespace.errors.noOwnerAfterwards'),
Expand Down
Loading