Commit f2ab8518 authored by nanahira's avatar nanahira

assets

parent 1ca4c2bc
This diff is collapsed.
......@@ -21,7 +21,9 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.26.0",
"@nestjs/common": "^8.0.0",
"@nestjs/config": "^1.0.1",
"@nestjs/core": "^8.0.0",
"@nestjs/platform-express": "^8.0.0",
"@nestjs/swagger": "^5.0.9",
......
......@@ -2,9 +2,7 @@ import {
Body,
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Post,
Put,
UploadedFile,
......
......@@ -2,14 +2,17 @@ import {
Body,
Controller,
Get,
ParseIntPipe,
Post,
Query,
UploadedFile,
UseGuards,
UseInterceptors,
ValidationPipe,
} from '@nestjs/common';
import { AppService } from './app.service';
import {
ApiBody,
ApiConsumes,
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
......@@ -18,14 +21,23 @@ import {
import {
BlankReturnMessageDto,
GetAppReturnMessageDto,
ReturnMessageDto,
StringReturnMessageDto,
} from './dto/ReturnMessage.dto';
import { FetchMyCardUser, MyCardUser } from './utility/mycard-auth';
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;
@Controller('api')
export class AppController {
constructor(private readonly appService: AppService) {}
constructor(
private readonly appService: AppService,
private readonly s3: S3Service,
) {}
@Get('apps.json')
getAppsJson() {
......@@ -55,4 +67,26 @@ export class AppController {
) {
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';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeormConfig } from './config';
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({
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],
providers: [AppService],
providers: [AppService, S3Service],
})
export class AppModule {}
import { Any, Connection, FindConditions, In, IsNull, Not } from 'typeorm';
import { Connection, IsNull, Not } from '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 { App } from './entities/App.entity';
import {
......@@ -22,7 +22,7 @@ export class AppService extends ConsoleLogger {
return (
await this.db
.getRepository(App)
.find({ where: { appContent: Not(IsNull()), isDeleted: false } })
.find({ where: { appData: Not(IsNull()), isDeleted: false } })
).map((a) => a.appData);
}
......@@ -84,6 +84,21 @@ export class AppService extends ConsoleLogger {
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) {
let app = await this.db
.getRepository(App)
......@@ -115,13 +130,11 @@ export class AppService extends ConsoleLogger {
throw new BlankReturnMessageDto(401, 'Needs login').toException();
}
appData.id = id;
const app = await this.db
.getRepository(App)
.findOne({
where: { id: appData.id },
relations: ['history'],
select: ['id', 'author', 'appContent'],
});
const app = await this.db.getRepository(App).findOne({
where: { id: appData.id },
relations: ['history'],
select: ['id', 'author', 'appData'],
});
if (!app) {
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 {
@ApiProperty({ description: '返回 app' })
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 {
h.appData = appData;
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 { Column } from 'typeorm';
import { AppsJson } from '../utility/apps-json-type';
import { MyCardUser } from '../utility/mycard-auth';
export class AppBase extends TimeBase {
@Column('text', { nullable: true })
appContent: string;
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);
}
@Column('jsonb', { nullable: true })
appData: AppsJson.App;
}
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
......
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { getUserFromContext } from './utility/mycard-auth';
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