Commit f2ab8518 authored by nanahira's avatar nanahira

assets

parent 1ca4c2bc
This diff is collapsed.
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.26.0",
"@nestjs/common": "^8.0.0", "@nestjs/common": "^8.0.0",
"@nestjs/config": "^1.0.1",
"@nestjs/core": "^8.0.0", "@nestjs/core": "^8.0.0",
"@nestjs/platform-express": "^8.0.0", "@nestjs/platform-express": "^8.0.0",
"@nestjs/swagger": "^5.0.9", "@nestjs/swagger": "^5.0.9",
......
...@@ -2,9 +2,7 @@ import { ...@@ -2,9 +2,7 @@ import {
Body, Body,
Controller, Controller,
Delete, Delete,
Get,
Param, Param,
ParseIntPipe,
Post, Post,
Put, Put,
UploadedFile, UploadedFile,
......
...@@ -2,14 +2,17 @@ import { ...@@ -2,14 +2,17 @@ import {
Body, Body,
Controller, Controller,
Get, Get,
ParseIntPipe,
Post, Post,
Query, Query,
UploadedFile,
UseGuards,
UseInterceptors,
ValidationPipe, ValidationPipe,
} from '@nestjs/common'; } from '@nestjs/common';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { import {
ApiBody, ApiBody,
ApiConsumes,
ApiCreatedResponse, ApiCreatedResponse,
ApiOkResponse, ApiOkResponse,
ApiOperation, ApiOperation,
...@@ -18,14 +21,23 @@ import { ...@@ -18,14 +21,23 @@ import {
import { import {
BlankReturnMessageDto, BlankReturnMessageDto,
GetAppReturnMessageDto, GetAppReturnMessageDto,
ReturnMessageDto,
StringReturnMessageDto,
} from './dto/ReturnMessage.dto'; } from './dto/ReturnMessage.dto';
import { FetchMyCardUser, MyCardUser } from './utility/mycard-auth'; import { FetchMyCardUser, MyCardUser } from './utility/mycard-auth';
import { AppsJson } from './utility/apps-json-type'; import { AppsJson } from './utility/apps-json-type';
import { MyCardAppMaintainerGuard } from './my-card-app-maintainer.guard';
import { S3Service } from './s3/s3.service';
import { FileInterceptor } from '@nestjs/platform-express';
import { FileUploadDto } from './dto/FileUpload.dto';
import AppClass = AppsJson.AppClass; import AppClass = AppsJson.AppClass;
@Controller('api') @Controller('api')
export class AppController { export class AppController {
constructor(private readonly appService: AppService) {} constructor(
private readonly appService: AppService,
private readonly s3: S3Service,
) {}
@Get('apps.json') @Get('apps.json')
getAppsJson() { getAppsJson() {
...@@ -55,4 +67,26 @@ export class AppController { ...@@ -55,4 +67,26 @@ export class AppController {
) { ) {
return this.appService.updateApp(user, app.id, app); return this.appService.updateApp(user, app.id, app);
} }
@Post('assets')
@ApiOperation({
summary: '上传附件',
description: '必须登录用户且必须是管理员或者拥有1个 app 才能上传',
})
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'apps.json 文件',
type: FileUploadDto,
})
@ApiCreatedResponse({ type: StringReturnMessageDto })
@UseGuards(MyCardAppMaintainerGuard)
async uploadAssets(@UploadedFile() file: Express.Multer.File) {
const res = await this.s3.uploadAssets(file);
if (res) {
return new ReturnMessageDto(201, 'success', res);
} else {
throw new BlankReturnMessageDto(500, 'upload fail').toException();
}
}
} }
...@@ -2,12 +2,36 @@ import { Module } from '@nestjs/common'; ...@@ -2,12 +2,36 @@ import { Module } from '@nestjs/common';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { typeormConfig } from './config';
import { AdminController } from './admin/admin.controller'; import { AdminController } from './admin/admin.controller';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { App } from './entities/App.entity';
import { AppHistory } from './entities/AppHistory.entity';
import { S3Service } from './s3/s3.service';
const configModule = ConfigModule.forRoot();
@Module({ @Module({
imports: [TypeOrmModule.forRoot(typeormConfig())], imports: [
configModule,
TypeOrmModule.forRootAsync({
name: 'app',
imports: [configModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => {
return {
type: 'postgres',
entities: [App, AppHistory], // entities here
synchronize: !config.get('DB_NO_INIT'),
host: config.get('DB_HOST'),
port: parseInt(config.get('DB_PORT')) || 5432,
username: config.get('DB_USER'),
password: config.get('DB_PASS'),
database: config.get('DB_NAME'),
};
},
}),
],
controllers: [AppController, AdminController], controllers: [AppController, AdminController],
providers: [AppService], providers: [AppService, S3Service],
}) })
export class AppModule {} export class AppModule {}
import { Any, Connection, FindConditions, In, IsNull, Not } from 'typeorm'; import { Connection, IsNull, Not } from 'typeorm';
import { InjectConnection } from '@nestjs/typeorm'; import { InjectConnection } from '@nestjs/typeorm';
import { Injectable, ConsoleLogger, HttpException } from '@nestjs/common'; import { ConsoleLogger, Injectable } from '@nestjs/common';
import { AppsJson } from './utility/apps-json-type'; import { AppsJson } from './utility/apps-json-type';
import { App } from './entities/App.entity'; import { App } from './entities/App.entity';
import { import {
...@@ -22,7 +22,7 @@ export class AppService extends ConsoleLogger { ...@@ -22,7 +22,7 @@ export class AppService extends ConsoleLogger {
return ( return (
await this.db await this.db
.getRepository(App) .getRepository(App)
.find({ where: { appContent: Not(IsNull()), isDeleted: false } }) .find({ where: { appData: Not(IsNull()), isDeleted: false } })
).map((a) => a.appData); ).map((a) => a.appData);
} }
...@@ -84,6 +84,21 @@ export class AppService extends ConsoleLogger { ...@@ -84,6 +84,21 @@ export class AppService extends ConsoleLogger {
return new ReturnMessageDto(200, 'success', await query.getMany()); return new ReturnMessageDto(200, 'success', await query.getMany());
} }
async isUserCanMaintainApp(user: MyCardUser, id?: string) {
if (user.admin) {
return true;
}
const query = this.db
.getRepository(App)
.createQueryBuilder('app')
.where('app.isDeleted = false')
.andWhere(':uid = ANY(app.author)', { uid: user.id });
if (id) {
query.andWhere('app.id = :id', { id });
}
return (await query.getCount()) > 0;
}
async createApp(id: string) { async createApp(id: string) {
let app = await this.db let app = await this.db
.getRepository(App) .getRepository(App)
...@@ -115,13 +130,11 @@ export class AppService extends ConsoleLogger { ...@@ -115,13 +130,11 @@ export class AppService extends ConsoleLogger {
throw new BlankReturnMessageDto(401, 'Needs login').toException(); throw new BlankReturnMessageDto(401, 'Needs login').toException();
} }
appData.id = id; appData.id = id;
const app = await this.db const app = await this.db.getRepository(App).findOne({
.getRepository(App) where: { id: appData.id },
.findOne({ relations: ['history'],
where: { id: appData.id }, select: ['id', 'author', 'appData'],
relations: ['history'], });
select: ['id', 'author', 'appContent'],
});
if (!app) { if (!app) {
throw new BlankReturnMessageDto(404, 'App not found').toException(); throw new BlankReturnMessageDto(404, 'App not found').toException();
} }
......
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { App } from './entities/App.entity';
import { AppHistory } from './entities/AppHistory.entity';
export function dbConfig() {
return {
host: process.env.DB_HOST,
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 5432,
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
};
}
export function typeormConfig(): TypeOrmModuleOptions {
return {
name: 'app',
type: 'postgres',
entities: [App, AppHistory], // entities here
synchronize: true,
...dbConfig(),
};
}
...@@ -33,3 +33,25 @@ export class GetAppReturnMessageDto extends BlankReturnMessageDto { ...@@ -33,3 +33,25 @@ export class GetAppReturnMessageDto extends BlankReturnMessageDto {
@ApiProperty({ description: '返回 app' }) @ApiProperty({ description: '返回 app' })
data?: AppsJson.AppClass; data?: AppsJson.AppClass;
} }
export class UploadAssignInfo {
@ApiProperty({ description: '下载地址' })
downloadUrl: string;
@ApiProperty({ description: 's3 上传地址,如果是空则不需要上传' })
uploadUrl?: string;
constructor(downloadUrl: string, uploadurl?: string) {
this.downloadUrl = downloadUrl;
this.uploadUrl = uploadurl;
}
}
export class StringReturnMessageDto extends BlankReturnMessageDto {
@ApiProperty({ description: '返回字符串' })
data?: string;
}
export class UploadAssignInfoReturnMessageDto extends BlankReturnMessageDto {
@ApiProperty({ description: '返回内容' })
data?: UploadAssignInfo;
}
...@@ -36,14 +36,4 @@ export class App extends AppBase { ...@@ -36,14 +36,4 @@ export class App extends AppBase {
h.appData = appData; h.appData = appData;
this.history.push(h); this.history.push(h);
} }
get appData(): AppsJson.App {
const appData = super.appData;
appData.id = this.id;
return appData;
}
set appData(a) {
this.appContent = JSON.stringify(a);
}
} }
import { TimeBase } from './TimeBase.entity'; import { TimeBase } from './TimeBase.entity';
import { Column } from 'typeorm'; import { Column } from 'typeorm';
import { AppsJson } from '../utility/apps-json-type'; import { AppsJson } from '../utility/apps-json-type';
import { MyCardUser } from '../utility/mycard-auth';
export class AppBase extends TimeBase { export class AppBase extends TimeBase {
@Column('text', { nullable: true }) @Column('jsonb', { nullable: true })
appContent: string; appData: AppsJson.App;
get appData(): AppsJson.App {
if (!this.appContent) {
return null;
}
const a = JSON.parse(this.appContent);
return a;
}
set appData(a) {
this.appContent = JSON.stringify(a);
}
} }
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { NestExpressApplication } from '@nestjs/platform-express'; import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
......
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { getUserFromContext } from './utility/mycard-auth'; import { getUserFromContext } from './utility/mycard-auth';
import { BlankReturnMessageDto } from './dto/ReturnMessage.dto'; import { BlankReturnMessageDto } from './dto/ReturnMessage.dto';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment