diff --git a/backend-nest/src/modules/instances/instances.controller.ts b/backend-nest/src/modules/instances/instances.controller.ts index 3d81f3a..d372472 100644 --- a/backend-nest/src/modules/instances/instances.controller.ts +++ b/backend-nest/src/modules/instances/instances.controller.ts @@ -64,4 +64,70 @@ export class InstancesController { ) { return this.instances.writeFile(licenseId, id, body.path, body.content ?? ''); } + + @Post(':id/files/delete') + @RequirePermission('files.manage') + @ApiOperation({ summary: 'Delete a file or directory (jailed)' }) + async deleteFile( + @CurrentTenant() licenseId: string, + @Param('id') id: string, + @Body() body: { path: string }, + ) { + return this.instances.deleteFile(licenseId, id, body.path); + } + + @Post(':id/files/rename') + @RequirePermission('files.manage') + @ApiOperation({ summary: 'Rename a file/directory within its parent (jailed)' }) + async renameFile( + @CurrentTenant() licenseId: string, + @Param('id') id: string, + @Body() body: { path: string; name: string }, + ) { + return this.instances.renameFile(licenseId, id, body.path, body.name); + } + + @Post(':id/files/mkdir') + @RequirePermission('files.manage') + @ApiOperation({ summary: 'Create a directory (jailed)' }) + async mkdir( + @CurrentTenant() licenseId: string, + @Param('id') id: string, + @Body() body: { path: string }, + ) { + return this.instances.mkdir(licenseId, id, body.path); + } + + @Post(':id/files/mkfile') + @RequirePermission('files.manage') + @ApiOperation({ summary: 'Create an empty file (jailed)' }) + async mkfile( + @CurrentTenant() licenseId: string, + @Param('id') id: string, + @Body() body: { path: string }, + ) { + return this.instances.mkfile(licenseId, id, body.path); + } + + @Post(':id/files/move') + @RequirePermission('files.manage') + @ApiOperation({ summary: 'Move a file/directory (jailed)' }) + async moveFile( + @CurrentTenant() licenseId: string, + @Param('id') id: string, + @Body() body: { path: string; dest: string }, + ) { + return this.instances.moveFile(licenseId, id, body.path, body.dest); + } + + @Post(':id/files/copy') + @RequirePermission('files.manage') + @ApiOperation({ summary: 'Copy a file/directory (jailed)' }) + async copyFile( + @CurrentTenant() licenseId: string, + @Param('id') id: string, + @Body() body: { path: string; dest: string }, + ) { + return this.instances.copyFile(licenseId, id, body.path, body.dest); + } } diff --git a/backend-nest/src/modules/instances/instances.service.ts b/backend-nest/src/modules/instances/instances.service.ts index 508eda4..3780c12 100644 --- a/backend-nest/src/modules/instances/instances.service.ts +++ b/backend-nest/src/modules/instances/instances.service.ts @@ -97,4 +97,49 @@ export class InstancesService { const res = await this.fileOp(licenseId, instanceId, { op: 'write', path, content }); return res.data ?? { status: 'success' }; } + + async deleteFile(licenseId: string, instanceId: string, path: string): Promise { + if (!path) throw new BadRequestException('path is required'); + return (await this.fileOp(licenseId, instanceId, { op: 'delete', path })).data ?? { ok: true }; + } + + async renameFile( + licenseId: string, + instanceId: string, + path: string, + name: string, + ): Promise { + if (!path || !name) throw new BadRequestException('path and name are required'); + return (await this.fileOp(licenseId, instanceId, { op: 'rename', path, name })).data ?? { ok: true }; + } + + async mkdir(licenseId: string, instanceId: string, path: string): Promise { + if (!path) throw new BadRequestException('path is required'); + return (await this.fileOp(licenseId, instanceId, { op: 'mkdir', path })).data ?? { ok: true }; + } + + async mkfile(licenseId: string, instanceId: string, path: string): Promise { + if (!path) throw new BadRequestException('path is required'); + return (await this.fileOp(licenseId, instanceId, { op: 'mkfile', path })).data ?? { ok: true }; + } + + async moveFile( + licenseId: string, + instanceId: string, + path: string, + dest: string, + ): Promise { + if (!path || !dest) throw new BadRequestException('path and dest are required'); + return (await this.fileOp(licenseId, instanceId, { op: 'move', path, dest })).data ?? { ok: true }; + } + + async copyFile( + licenseId: string, + instanceId: string, + path: string, + dest: string, + ): Promise { + if (!path || !dest) throw new BadRequestException('path and dest are required'); + return (await this.fileOp(licenseId, instanceId, { op: 'copy', path, dest })).data ?? { ok: true }; + } }