Merge branch 'feature' of https://github.com/vanillabrain/vanillameta into feature

This commit is contained in:
SA K
2022-12-13 11:10:30 +09:00
23 changed files with 405 additions and 84 deletions

View File

@@ -2932,15 +2932,27 @@
}
},
"@nestjs/swagger": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-6.1.2.tgz",
"integrity": "sha512-RU1DeTDyuN/lRXKFWaf7I9LYF34/ale3IIGeY3romAcXL/N9W0+50Ek3ou+Ajd5FqpLqzt7saYhnaQegVuU4UQ==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-6.1.4.tgz",
"integrity": "sha512-kE8VjR+NaoKqxg8XqM/YYfALScPh4AcoR8Wywga8/OxHsTHY+MKxqvTpWp7IhCUWSA6xT8nQUpcC9Rt7C+r7Hw==",
"requires": {
"@nestjs/mapped-types": "1.1.0",
"@nestjs/mapped-types": "1.2.0",
"js-yaml": "4.1.0",
"lodash": "4.17.21",
"path-to-regexp": "3.2.0",
"swagger-ui-dist": "4.14.0"
"swagger-ui-dist": "4.15.5"
},
"dependencies": {
"@nestjs/mapped-types": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.2.0.tgz",
"integrity": "sha512-NTFwPZkQWsArQH8QSyFWGZvJ08gR+R4TofglqZoihn/vU+ktHEJjMqsIsADwb7XD97DhiD+TVv5ac+jG33BHrg=="
},
"swagger-ui-dist": {
"version": "4.15.5",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.15.5.tgz",
"integrity": "sha512-V3eIa28lwB6gg7/wfNvAbjwJYmDXy1Jo1POjyTzlB6wPcHiGlRxq39TSjYGVjQrUSAzpv+a7nzp7mDxgNy57xA=="
}
}
},
"@nestjs/testing": {
@@ -7928,6 +7940,11 @@
}
}
},
"encoding-negotiator": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/encoding-negotiator/-/encoding-negotiator-2.0.1.tgz",
"integrity": "sha512-GSK7qphNR4iPcejfAlZxKDoz3xMhnspwImK+Af5WhePS9jUpK/Oh7rUdyENWu+9rgDflOCTmAojBsgsvM8neAQ=="
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -8786,6 +8803,130 @@
"integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
"dev": true
},
"fastify-plugin": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.1.tgz",
"integrity": "sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA=="
},
"fastify-static": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/fastify-static/-/fastify-static-4.7.0.tgz",
"integrity": "sha512-zZhCfJv/hkmud2qhWqpU3K9XVAuy3+IV8Tp9BC5J5U+GyA2XwoB6h8lh9GqpEIqdXOw01WyWQllV7dOWVyAlXg==",
"requires": {
"fastify-static-deprecated": "npm:fastify-static@4.6.1",
"process-warning": "^1.0.0"
}
},
"fastify-static-deprecated": {
"version": "npm:fastify-static@4.6.1",
"resolved": "https://registry.npmjs.org/fastify-static/-/fastify-static-4.6.1.tgz",
"integrity": "sha512-vy7N28U4AMhuOim12ZZWHulEE6OQKtzZbHgiB8Zj4llUuUQXPka0WHAQI3njm1jTCx4W6fixUHfpITxweMtAIA==",
"requires": {
"content-disposition": "^0.5.3",
"encoding-negotiator": "^2.0.1",
"fastify-plugin": "^3.0.0",
"glob": "^7.1.4",
"p-limit": "^3.1.0",
"readable-stream": "^3.4.0",
"send": "^0.17.1"
},
"dependencies": {
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg=="
},
"http-errors": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.1"
}
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
"requires": {
"ee-first": "1.1.1"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"send": {
"version": "0.17.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
"integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "1.8.1",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="
}
}
},
"fastify-swagger": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/fastify-swagger/-/fastify-swagger-5.2.0.tgz",
"integrity": "sha512-yKct50Mev9YIrhd2FRO4AChcJM9JwTBCziIjA4C+AI+hV2ystaIklgHVEwHoyqlaeQ+B4gZ1Z5rgOE87i4llLg==",
"requires": {
"fastify-swagger-deprecated": "npm:fastify-swagger@5.1.1",
"process-warning": "^1.0.0"
},
"dependencies": {
"fastify-swagger-deprecated": {
"version": "npm:fastify-swagger@5.1.1",
"resolved": "https://registry.npmjs.org/fastify-swagger/-/fastify-swagger-5.1.1.tgz",
"integrity": "sha512-7DA0zS8CCV5r+gbLgWdeeKEwLrVbbOxLMJVUfOl1H9+wSildSLD8hok2TLX7s3c28wOjF8+iZRxsz/hBDzfdIw==",
"requires": {
"fastify-plugin": "^3.0.0",
"fastify-static": "^4.0.0",
"js-yaml": "^4.0.0",
"json-schema-resolver": "^1.3.0",
"openapi-types": "^10.0.0",
"rfdc": "^1.3.0"
}
}
}
},
"fastq": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
@@ -11487,6 +11628,31 @@
}
}
},
"json-schema-resolver": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-1.3.0.tgz",
"integrity": "sha512-EX7W1r8aZ/T3j8GbbBxPXi60bnsELfT90OiA1QrbGMvwzVSbyMNOAzvMFcFb8m7gKCXZLJpGe+cJOvWgoFl29A==",
"requires": {
"debug": "^4.1.1",
"rfdc": "^1.1.4",
"uri-js": "^4.2.2"
},
"dependencies": {
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
@@ -13309,6 +13475,11 @@
"is-wsl": "^2.1.1"
}
},
"openapi-types": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-10.0.0.tgz",
"integrity": "sha512-Y8xOCT2eiKGYDzMW9R4x5cmfc3vGaaI4EL2pwhDmodWw1HlK18YcZ4uJxc7Rdp7/gGzAygzH9SXr6GKYIXbRcQ=="
},
"optional": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/optional/-/optional-0.1.4.tgz",
@@ -13417,7 +13588,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"requires": {
"yocto-queue": "^0.1.0"
}
@@ -14254,6 +14424,11 @@
"type": "^2.1.0"
}
},
"process-warning": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz",
"integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q=="
},
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -14931,6 +15106,11 @@
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true
},
"rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
},
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -17796,7 +17976,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
@@ -18375,8 +18554,7 @@
"yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
},
"zip-stream": {
"version": "4.1.0",

View File

@@ -33,7 +33,7 @@
"@nestjs/mapped-types": "*",
"@nestjs/passport": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/swagger": "^6.1.2",
"@nestjs/swagger": "^6.1.4",
"@nestjs/typeorm": "^9.0.1",
"@nestjsplus/knex": "^1.0.0",
"@types/cookie-parser": "^1.4.3",
@@ -48,6 +48,7 @@
"cookie-parser": "^1.4.6",
"cross-env": "^7.0.3",
"dylib-node": "^1.0.10",
"fastify-swagger": "^5.2.0",
"js-joda": "^1.11.0",
"knex": "^2.3.0",
"knex-bigquery": "^2.0.3",

View File

@@ -28,10 +28,9 @@ import { ShareUrlModule } from './share-url/share-url.module';
port: parseInt(process.env.DB_PORT) || 3306,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: 'vanillameta_auth',
database: process.env.DB_NAME,
autoLoadEntities: true,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
// entities: ["dist/common/entities/*{.ts,.js}"],
synchronize: false,
logging: process.env.NODE_ENV == 'prod',
retryAttempts: 1,

View File

@@ -43,7 +43,7 @@ export class AuthService {
// accesstoken이 없을때
}
async setRefreshKey(refreshToken: string, jwt_id: string){
async setRefreshKey(refreshToken: string, jwt_id: number){
const findToken = await this.refreshTokenRepository.findOne({ where: { id: jwt_id } });
const token = refreshToken.replace('Bearer ', '');
if(!findToken){
@@ -68,12 +68,13 @@ export class AuthService {
// 회원이 존재하는지 확인
}
async deleteRefreshToken(Token: string) {
const token = Token.replace('Bearer ', '').split('=')[1];
async deleteRefreshToken(userId: number) {
console.log(userId)
const refreshTokenInfo = await this.refreshTokenRepository.findOne({
where: { refreshToken: token },
where: { id: userId },
});
refreshTokenInfo.refreshToken = null;
console.log(refreshTokenInfo)
refreshTokenInfo.refreshToken = '';
await this.refreshTokenRepository.save(refreshTokenInfo);
}
@@ -99,8 +100,7 @@ export class AuthService {
}
} // Refresh 토큰이 유효한지 확인
async checkAccess(token: string, password: string) {
const { accessKeyData } = await this.verifyAccessToken(token);
return await this.validateUser(accessKeyData.userId, password)
async checkAccess(userId: string, password: string) {
return await this.validateUser(userId, password)
}
}

View File

@@ -3,7 +3,7 @@ import { Column, Entity, PrimaryColumn } from 'typeorm';
@Entity()
export class RefreshToken {
@PrimaryColumn()
id: string;
id: number;
@Column()
refreshToken: string;

View File

@@ -15,6 +15,7 @@ export class JwtAuthGuard extends AuthGuard('jwt') {
}
const token = authorization.replace('Bearer ', '');
const userInfo = await this.validate(token);
if (userInfo) request.user = userInfo;
return !!userInfo;
}

View File

@@ -11,15 +11,15 @@ export class DashboardController {
@UseGuards(JwtAuthGuard)
@Post()
create(@Body() createDashboardDto: CreateDashboardDto, @Req() req) {
const { authorization } = req.headers;
return this.dashboardService.create(createDashboardDto, authorization);
const { accessKeyData } = req.user
return this.dashboardService.create(createDashboardDto, accessKeyData.id);
}
@UseGuards(JwtAuthGuard)
@Get()
findAll(@Req() req) {
const { authorization } = req.headers;
return this.dashboardService.findAll(authorization);
const { accessKeyData } = req.user;
return this.dashboardService.findAll(accessKeyData.id);
}
@UseGuards(JwtAuthGuard)

View File

@@ -30,11 +30,9 @@ export class DashboardService {
private readonly authService: AuthService,
) {}
async create(createDashboardDto: CreateDashboardDto, accessToken: string) {
const findUser = await this.authService.verifyAccessToken(accessToken);
const { accessKeyData } = findUser;
async create(createDashboardDto: CreateDashboardDto, accessToken: number) {
const userData = await this.userRepository.findOne({
where: { id: accessKeyData.id },
where: { id: accessToken },
});
if (!userData) {
return 'Bad Request';
@@ -53,7 +51,7 @@ export class DashboardService {
if (findTitle.title !== createDashboardDto.title) {
const share_id = await this.dashboardShareRepository.save({
uuid: uuidv4(),
uuid: uuidv4(), // uuid의 버전 uuidv1의 결우 mac의 정보등을 담고있음.
createdAt: new Date(),
updatedAt: new Date()
});
@@ -81,6 +79,7 @@ export class DashboardService {
// .orUpdate(['title', 'layout'], ['id'])
// .execute();
// console.log('test', test.generatedMaps[0].id);
// 기존 코드
await this.userService.saveDashboard(newDashboard.id, id);
await this.dashboardWidgetService.create(saveObjDW);
@@ -91,8 +90,8 @@ export class DashboardService {
return 'already exist name title';
}
async findAll(accessToken: string) {
const findUser = await this.userService.findDashboardId(accessToken);
async findAll(userId: number) {
const findUser = await this.userService.findDashboardId(userId);
const findId = findUser.map(el =>
el['dashboardId']);
const find_all = [];
@@ -149,6 +148,7 @@ export class DashboardService {
dashboardId: id,
widgetIds: widgetIds,
};
// 업데이트할 데이터
await this.dashboardWidgetService.update(id, saveObjDW);
const updatedDashboard = await this.dashboardRepository.save(find_dashboard);

View File

@@ -6,7 +6,10 @@ import { LoginUserDto } from '../login/dto/login-user.dto';
import { AuthService } from 'src/auth/auth.service';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { LocalAuthGuard } from 'src/auth/guards/local-auth.guard';
import {ApiOperation, ApiTags } from '@nestjs/swagger';
@ApiTags('로그인 관련 API')
@Controller('login')
export class LoginController {
constructor(
@@ -16,6 +19,7 @@ export class LoginController {
@UsePipes(ValidationPipe)
@Post('signin')
@ApiOperation({ summary: ' 로그인 ' })
async logIn(@Res() res,@Req() req, @Body() loginDto: LoginUserDto){
const findUser = await this.loginService.signin(loginDto)
// 유저존재여부 확인
@@ -27,25 +31,27 @@ export class LoginController {
// RefreshToken 저장
res.cookie('jwt_re', refreshToken, {
httpOnly: true,
sameSite: 'none',
secure: true
httpOnly: true, // 브라우저에서 cookie에 에 접근권한 x
sameSite: 'Lax', // 다른 도메인의 cookie를 허용한 주소만 가져올 수 있음
secure: true // 보안처리된 https만 허
})
return res.status(201).json({ accessToken: accessToken, message: 'success' })
}
// validationPipe = 들어오는 모든 클라이언트 페이로드에 대한 유효성 검사 규칙을 적용
@UsePipes(ValidationPipe)
@Post('signup')
@ApiOperation({ summary: '회원가입' })
create(@Body() createUserDto: CreateLoginDto) {
return this.loginService.signup(createUserDto);
}
@UseGuards(LocalAuthGuard)
@UseGuards(LocalAuthGuard) //refrshtoken 검사
@Post('signout')
@ApiOperation({ summary: '로그아웃' })
async signOut(@Res() res, @Req() req) {
const refreshToken = req.headers.cookie;
delete req.headers.authorization;
await this.authService.deleteRefreshToken(refreshToken)
const { jwtId } = req.user.refreshKeyData;
await this.authService.deleteRefreshToken(jwtId)
return res.status(201).clearCookie('jwt_re').json({ message: 'success'})
}
}

View File

@@ -19,7 +19,7 @@ export class LoginService {
async signin(loginDto: LoginUserDto) {
const { userId, password } = loginDto;
const findUser = await this.authService.validateUser(userId, password);
const findUser = await this.authService.validateUser(userId, password); // 요저의 존재여부 확인
if (!findUser) {
throw new UnauthorizedException(`Unauthorized`);
}

View File

@@ -5,13 +5,13 @@ import { ExpressAdapter } from '@nestjs/platform-express';
import { HttpExceptionFilter } from './nest-utils/http-exception.filter';
import { ValidationPipe } from '@nestjs/common';
import cookieParser from 'cookie-parser';
import { setupSwagger } from './utils/swagger.js';
async function bootstrap() {
const expressApp = express();
const whiteList = ['https://vanillameta-dev.vanillabrain.com', 'http://localhost:3000']
const whiteList = ['https://vanillameta-dev.vanillabrain.com', 'http://localhost:3000', 'http://localhost:4000']
const app = await NestFactory.create(AppModule, new ExpressAdapter(expressApp), {
logger: console,
cors: {
origin: function (origin, callback){
if(whiteList.indexOf(origin) !== -1){
@@ -29,8 +29,7 @@ async function bootstrap() {
});
app.useGlobalFilters(new HttpExceptionFilter());
app.use(cookieParser());
// app.useGlobalPipes(new ValidationPipe({ transform: true }));
// const app = await NestFactory.create(AppModule);
setupSwagger(app)
await app.listen(4000);
}

View File

@@ -1,11 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class ShareUrlOnDto {
@IsString()
@IsNotEmpty()
@ApiProperty({ description: '유저Id'})
userId: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ description: '공유 url 공유기간'})
endDate: string;
}

View File

@@ -3,28 +3,33 @@ import { ShareUrlService } from './share-url.service';
import { ShareUrlOnDto } from './dto/create-share-url.dto';
import { UpdateShareUrlDto } from './dto/update-share-url.dto';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import {ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
@Controller('share-url')
@ApiTags('url공유화 API')
export class ShareUrlController {
constructor(private readonly shareUrlService: ShareUrlService) {}
@UseGuards(JwtAuthGuard)
@Post('share-on/:dashboardId')
@ApiOperation({ summary: '공유 url On' })
checkShareUrlOn(@Req() req, @Param() params, @Body() shareUrlOnDto: ShareUrlOnDto) {
const { authorization } = req.headers;
const { userId } = req.user.accessKeyData;
const { dashboardId } = params;
return this.shareUrlService.checkShareUrlOn( authorization, dashboardId, shareUrlOnDto )
return this.shareUrlService.checkShareUrlOn( userId, dashboardId, shareUrlOnDto )
}
@UseGuards(JwtAuthGuard)
@Post('share-off/:dashboardId')
@ApiOperation({ summary: '공유 url Off' })
checkShareUrlOff(@Req() req, @Param() params, @Body() shareUrlOnDto: ShareUrlOnDto) {
const { authorization } = req.headers;
const { userId } = req.user.accessKeyData;
const { dashboardId } = params;
return this.shareUrlService.checkShareUrlOff( authorization, dashboardId, shareUrlOnDto)
return this.shareUrlService.checkShareUrlOff( userId, dashboardId, shareUrlOnDto)
}
@Get('share-dashboard/:uuid')
@ApiOperation({ summary: '공유 url 접속' })
async shareDashboardInfo(@Param() param) {
const { shareUrl } = param;
return await this.shareUrlService.shareDashboardInfo(shareUrl)

View File

@@ -22,9 +22,8 @@ export class ShareUrlService {
}
async checkShareUrlOn( accessToken:string, dashboardId: number, shareUrlOnDto: ShareUrlOnDto ) {
const findpass = await this.userRepository.findOne({ where: { userId: shareUrlOnDto.userId }})
const findUser = await this.authService.checkAccess(accessToken, findpass.password);
async checkShareUrlOn( userId:string, dashboardId: number, shareUrlOnDto: ShareUrlOnDto ) {
const findUser = await this.userRepository.findOne({ where: { userId: userId }})
if(!findUser){
return 'not exist user'
} else {
@@ -40,22 +39,23 @@ export class ShareUrlService {
return { uuid: findDashboardShare.uuid, message: "success" }
}
}
// 공유기능 on시 공유토큰과 endDate를 저장
async checkShareUrlOff( accessToken:string, dashboardId: number, shareUrlOnDto: ShareUrlOnDto){
const findpass = await this.userRepository.findOne({ where: { userId: shareUrlOnDto.userId }})
const findUser = await this.authService.checkAccess(accessToken, findpass.password);
async checkShareUrlOff( userId:string, dashboardId: number, shareUrlOnDto: ShareUrlOnDto){
const findUser = await this.userRepository.findOne({ where: { userId: userId }})
if(!findUser){
return 'not exist user'
} else {
const findDashboard = await this.dashboardRepository.findOne( { where: { id: dashboardId } })
const findDashboardShare = await this.dashboardShareRepository.findOne({ where: { id: findDashboard.shareId }})
findDashboardShare.shareToken = null;
findDashboardShare.shareToken = '';
findDashboardShare.shareYn = YesNo.NO;
findDashboardShare.endDate = null;
await this.dashboardShareRepository.save(findDashboardShare)
return { message: "success" }
}
}
// 공유기능 off시 쉐어토큰, endDate을 없애고 사용가능여부를 N으로 저장
async shareDashboardInfo(uuid: string){
const findDashboardShareUrl = await this.dashboardShareRepository.findOne({ where: { uuid: uuid }})
@@ -65,4 +65,5 @@ export class ShareUrlService {
}
return this.dashboardService.findOne(+findDashboard.id)
}
// 공유url로 접속시 대시보드의 정보를 받아오는 코드
}

View File

@@ -1,16 +1,21 @@
import { ApiProperty, ApiTags } from '@nestjs/swagger';
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
@ApiTags('유저생성 Dto')
export class CreateUserDto {
@IsString()
@IsNotEmpty()
@ApiProperty({ description: '유저Id'})
userId: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ description: '유저password'})
password: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ description: '유저email'})
email: string;
}

View File

@@ -1,3 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsOptional } from 'class-validator';
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn} from 'typeorm';
import { BaseEntity } from '../../common/entities/base.entity';
@@ -5,13 +6,16 @@ import { BaseEntity } from '../../common/entities/base.entity';
export class UserMapping extends BaseEntity {
@PrimaryGeneratedColumn()
@ApiProperty({ description: 'id'})
id: number;
@IsOptional()
@Column()
@ApiProperty({ description: '대시보드id'})
dashboardId: number;
@Column()
@ApiProperty({ description: '유저id'})
userInfoId: number;
// @IsOptional()

View File

@@ -1,3 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsOptional } from 'class-validator';
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, BaseEntity} from 'typeorm';
@@ -6,21 +7,26 @@ import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateCol
export class User {
@PrimaryGeneratedColumn()
@ApiProperty({ description: 'id'})
id: number;
@IsNotEmpty()
@Column()
@ApiProperty({ description: '유저Id'})
userId: string;
@Column()
jwtId: string;
@ApiProperty({ description: 'refreshTokenId'})
jwtId: number;
@IsNotEmpty()
@Column()
@ApiProperty({ description: '유저email'})
email: string;
@IsNotEmpty()
@Column()
@ApiProperty({ description: '유저password'})
password: string;
@CreateDateColumn({ default: () => 'CURRENT_TIMESTAMP', type: "timestamp" })

View File

@@ -4,8 +4,11 @@ import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { AuthService } from 'src/auth/auth.service';
import { LocalAuthGuard } from 'src/auth/guards/local-auth.guard';
import {ApiOperation, ApiTags } from '@nestjs/swagger';
@Controller('user')
@ApiTags('유저 API')
export class UserController {
constructor(
private readonly userService: UserService,
@@ -14,38 +17,43 @@ export class UserController {
@UseGuards(JwtAuthGuard)
@Get('userinfo')
@ApiOperation({ summary: '해당유저정보 가져오기' })
findOne(@Req() req) {
const { authorization } = req.headers;
return this.userService.findOne(authorization);
const { id } = req.user.accessKeyData;
return this.userService.findOne(id);
}
@UseGuards(JwtAuthGuard)
@Patch('change-info')
@ApiOperation({ summary: '유저정보 수정' })
updateUsername(@Req() req, @Body() updateUserDto: UpdateUserDto) {
const { authorization } = req.headers;
return this.userService.updateUserInfo(authorization, updateUserDto);
const { userId } = req.user.accessKeyData;
return this.userService.updateUserInfo(userId, updateUserDto);
}
@UseGuards(JwtAuthGuard)
@Delete('delete-account')
@ApiOperation({ summary: ' 해당유저 삭제 ' })
deleteUser(@Req() req, @Body() createUserDto: CreateUserDto) {
const { authorization } = req.headers;
const { userId } = req.user.accessKeyData;
const { password } = createUserDto;
return this.userService.deleteUser(authorization, password);
return this.userService.deleteUser(userId, password);
}
// @UseGuards(JwtAuthGuard)
@UseGuards(LocalAuthGuard)
@Post('get-access-token')
@ApiOperation({ summary: 'AccessToken 재 발급' })
async reissuanceAccessToken(@Req() req, @Res() res) {
const { cookie } = req.headers;
const accessToken = await this.userService.reissuanceAccessToken(cookie);
const { userId } = req.user.refreshKeyData;
const accessToken = await this.userService.reissuanceAccessToken(userId);
return res.status(201).json({ accessToken: accessToken, message: 'success' });
}
@UseGuards(JwtAuthGuard)
@Get('get-dashboard')
@ApiOperation({ summary: '해당유정의 대시보드 목록 가져오기' })
async findDashboardId(@Req() req) {
const { authorization } = req.headers;
await this.userService.findDashboardId(authorization);
const { id } = req.headers.accessKeyData;
await this.userService.findDashboardId(id);
}
}

View File

@@ -18,11 +18,9 @@ export class UserService {
private authService: AuthService,
) {}
async findOne(accessToken: string) {
const findUser = await this.authService.verifyAccessToken(accessToken)
const { accessKeyData } = findUser;
async findOne(userId: number) {
const userData = await this.userRepository.findOne({
where: { userId: accessKeyData.userId }
where: { id: userId }
});
if(!userData){
return 'Bad Request'
@@ -32,8 +30,8 @@ export class UserService {
}
}
async updateUserInfo(accessToken: string, updateUserDto: UpdateUserDto) {
const findUser = await this.authService.checkAccess(accessToken, updateUserDto.password);
async updateUserInfo(userId: string, updateUserDto: UpdateUserDto) {
const findUser = await this.authService.checkAccess(userId, updateUserDto.password);
if(!findUser){
throw new HttpException('not exist user', HttpStatus.CONFLICT);
} else {
@@ -44,8 +42,8 @@ export class UserService {
}
}
async deleteUser( accessToken:string, password: string ) {
const findUser = await this.authService.checkAccess(accessToken, password);
async deleteUser( userId:string, password: string ) {
const findUser = await this.authService.checkAccess(userId, password);
if(!findUser){
return 'Unauthorized'
} else {
@@ -54,30 +52,27 @@ export class UserService {
}
}
async reissuanceAccessToken(cookie: string){
const tokenInfo = await this.authService.verifyRefreshToken(cookie)
const { userId } = tokenInfo.refreshKeyData
async reissuanceAccessToken(userId: string){
const payload = await this.userRepository.findOne({ where: { userId: userId }})
return await this.authService.generateAccessToken(payload)
}
// AccessToken 만료시 재발급 코드
async saveDashboard(dashboardId: number, userInfoId: number) {
console.log(userInfoId)
const saveObj = {
dashboardId: dashboardId,
userInfoId: userInfoId
}
await this.userMappingRepository.save(saveObj)
}
// mapping table 대시보드id, 유저id 저장
async findDashboardId(accesstoken: string){
const findUser = await this.authService.verifyAccessToken(accesstoken)
const { accessKeyData } = findUser;
async findDashboardId(id: number){
const findDashboard = await this.userMappingRepository
.createQueryBuilder('user_mapping')
.select("dashboardId")
.where('user_mapping.userInfoId = :userInfoId', { userInfoId: accessKeyData.id }).getRawMany()
.where('user_mapping.userInfoId = :userInfoId', { userInfoId: id }).getRawMany()
return findDashboard
}
// 대시보드id찾는 코드
}

View File

@@ -0,0 +1,30 @@
import { OpenAPIObject } from '@nestjs/swagger';
import { PathsObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
const arrayIntersect = (array1: string[], array2: string[]): string[] => array1.filter((item) => array2.includes(item));
export const filterDocumentsPathsByTags = (publicDocument: OpenAPIObject): PathsObject => {
const result: PathsObject = {};
const tags = publicDocument.tags.map(({ name }) => name); // 모든 tage의 이름
for (const path of Object.keys(publicDocument.paths)) {
const pathMethods = {};
for (const method of Object.keys(publicDocument.paths[path])) {
const endpointTags = publicDocument.paths[path][method].tags; //addTag로 경로 설정한 테그이름
console.log(publicDocument.components)
if (!Array.isArray(endpointTags)) {
continue;
}
if (arrayIntersect(tags, endpointTags).length > 0) {
pathMethods[method] = publicDocument.paths[path][method]; // 모든 tag의 이름과 경로 설정한 테그이름이 같은것.
}
}
result[path] = pathMethods;
}
return result;
};

View File

@@ -0,0 +1,34 @@
import { INestApplication } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { filterDocumentsPathsByTags } from './swagger-APIhelper';
import { filterDocumentsDtoPathsByTags } from './swagger/Dtohelper'
/**
* Swagger 세팅
*
* @param {INestApplication} app
*/
export function setupSwagger(app: INestApplication): void {
const options = new DocumentBuilder()
.setTitle('NestJS Study API Docs')
.setDescription('NestJS Study API description')
.setVersion('3.0.3')
.addBearerAuth({
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
name: 'jwt_ac',
description: 'Enter jwt token',
in: 'header'
},'AccessKey')
.addTag('유저 API')
.addTag('url공유화 API')
.addTag('로그인 관련 API')
.build();
const document = SwaggerModule.createDocument(app, options);
document.paths = filterDocumentsPathsByTags(document);
// document.paths = filterDocumentsDtoPathsByTags(document);
SwaggerModule.setup('api-docs', app, document);
}

View File

@@ -0,0 +1,30 @@
import { OpenAPIObject } from '@nestjs/swagger';
import { ComponentsObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
const arrayIntersect = (array1: string[], array2: string[]): string[] => array1.filter((item) => array2.includes(item));
export const filterDocumentsDtoPathsByTags = (publicDocument: OpenAPIObject): ComponentsObject => {
const result: ComponentsObject = {};
const tags = publicDocument.tags.map(({ name }) => name); // 모든 tage의 이름
console.log(tags)
for (const path of Object.keys(publicDocument.paths)) {
const pathMethods = {};
for (const method of Object.keys(publicDocument.paths[path])) {
const endpointTags = publicDocument.paths[path][method].tags; //addTag로 경로 설정한 테그이름
if (!Array.isArray(endpointTags)) {
continue;
}
if (arrayIntersect(tags, endpointTags).length > 0) {
pathMethods[method] = publicDocument.paths[path][method]; // 모든 tag의 이름과 경로 설정한 테그이름이 같은것.
}
}
console.log(result)
result[path] = pathMethods;
}
return result;
};

16
landing-page/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,16 @@
pipeline {
agent any
tools {
git "git"
}
stages {
stage('deploy') {
steps {
sh "aws configure set region $AWS_DEFAULT_REGION"
sh "aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID"
sh "aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY"
sh "aws s3 cp landing-page s3://vanillameta.net --recursive --acl public-read"
}
}
}
}