mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 04:48:53 +00:00
feat(storage): binding jwst storage to node (#2808)
This commit is contained in:
27
apps/server/src/modules/workspaces/controller.ts
Normal file
27
apps/server/src/modules/workspaces/controller.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Storage } from '@affine/storage';
|
||||
import { Controller, Get, NotFoundException, Param, Res } from '@nestjs/common';
|
||||
import type { Response } from 'express';
|
||||
|
||||
@Controller('/api/workspaces')
|
||||
export class WorkspacesController {
|
||||
constructor(private readonly storage: Storage) {}
|
||||
|
||||
@Get('/:id/blobs/:name')
|
||||
async blob(
|
||||
@Param('id') workspaceId: string,
|
||||
@Param('name') name: string,
|
||||
@Res() res: Response
|
||||
) {
|
||||
const blob = await this.storage.blob(workspaceId, name);
|
||||
|
||||
if (!blob) {
|
||||
throw new NotFoundException('Blob not found');
|
||||
}
|
||||
|
||||
res.setHeader('content-type', blob.contentType);
|
||||
res.setHeader('last-modified', blob.lastModified);
|
||||
res.setHeader('content-length', blob.size);
|
||||
|
||||
res.send(blob.data);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspacesController } from './controller';
|
||||
import { PermissionService } from './permission';
|
||||
import { WorkspaceResolver } from './resolver';
|
||||
|
||||
@Module({
|
||||
providers: [WorkspaceResolver, PermissionService],
|
||||
providers: [WorkspaceResolver, PermissionService, WorkspacesController],
|
||||
exports: [PermissionService],
|
||||
})
|
||||
export class WorkspaceModule {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Storage } from '@affine/storage';
|
||||
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
Args,
|
||||
@@ -16,8 +17,11 @@ import {
|
||||
Resolver,
|
||||
} from '@nestjs/graphql';
|
||||
import type { User, Workspace } from '@prisma/client';
|
||||
// @ts-expect-error graphql-upload is not typed
|
||||
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
|
||||
|
||||
import { PrismaService } from '../../prisma';
|
||||
import type { FileUpload } from '../../types';
|
||||
import { Auth, CurrentUser } from '../auth';
|
||||
import { UserType } from '../users/resolver';
|
||||
import { PermissionService } from './permission';
|
||||
@@ -55,7 +59,8 @@ export class UpdateWorkspaceInput extends PickType(
|
||||
export class WorkspaceResolver {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly permissionProvider: PermissionService
|
||||
private readonly permissionProvider: PermissionService,
|
||||
private readonly storage: Storage
|
||||
) {}
|
||||
|
||||
@ResolveField(() => Permission, {
|
||||
@@ -174,8 +179,25 @@ export class WorkspaceResolver {
|
||||
@Mutation(() => WorkspaceType, {
|
||||
description: 'Create a new workspace',
|
||||
})
|
||||
async createWorkspace(@CurrentUser() user: User) {
|
||||
return this.prisma.workspace.create({
|
||||
async createWorkspace(
|
||||
@CurrentUser() user: User,
|
||||
@Args({ name: 'init', type: () => GraphQLUpload })
|
||||
update: FileUpload
|
||||
) {
|
||||
// convert stream to buffer
|
||||
const buffer = await new Promise<Buffer>((resolve, reject) => {
|
||||
const stream = update.createReadStream();
|
||||
const chunks: Uint8Array[] = [];
|
||||
stream.on('data', chunk => {
|
||||
chunks.push(chunk);
|
||||
});
|
||||
stream.on('error', reject);
|
||||
stream.on('end', () => {
|
||||
resolve(Buffer.concat(chunks));
|
||||
});
|
||||
});
|
||||
|
||||
const workspace = await this.prisma.workspace.create({
|
||||
data: {
|
||||
public: false,
|
||||
users: {
|
||||
@@ -191,6 +213,10 @@ export class WorkspaceResolver {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await this.storage.createWorkspace(workspace.id, buffer);
|
||||
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@Mutation(() => WorkspaceType, {
|
||||
@@ -221,8 +247,15 @@ export class WorkspaceResolver {
|
||||
},
|
||||
});
|
||||
|
||||
await this.prisma.userWorkspacePermission.deleteMany({
|
||||
where: {
|
||||
workspaceId: id,
|
||||
},
|
||||
});
|
||||
|
||||
// TODO:
|
||||
// delete all related data, like websocket connections, blobs, etc.
|
||||
await this.storage.deleteWorkspace(id);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -283,4 +316,28 @@ export class WorkspaceResolver {
|
||||
|
||||
return this.permissionProvider.revoke(workspaceId, user.id);
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
async uploadBlob(
|
||||
@CurrentUser() user: User,
|
||||
@Args('workspaceId') workspaceId: string,
|
||||
@Args({ name: 'blob', type: () => GraphQLUpload })
|
||||
blob: FileUpload
|
||||
) {
|
||||
await this.permissionProvider.check(workspaceId, user.id);
|
||||
|
||||
const buffer = await new Promise<Buffer>((resolve, reject) => {
|
||||
const stream = blob.createReadStream();
|
||||
const chunks: Uint8Array[] = [];
|
||||
stream.on('data', chunk => {
|
||||
chunks.push(chunk);
|
||||
});
|
||||
stream.on('error', reject);
|
||||
stream.on('end', () => {
|
||||
resolve(Buffer.concat(chunks));
|
||||
});
|
||||
});
|
||||
|
||||
return this.storage.uploadBlob(workspaceId, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user