Skip to content
Snippets Groups Projects
Unverified Commit a34ddbd4 authored by Denis's avatar Denis Committed by GitHub
Browse files

Merge pull request #8 from ivanalglave/pdfback

Pdfback
parents ceefad6e 2eb4afa9
Branches
No related tags found
No related merge requests found
...@@ -105,3 +105,6 @@ dist ...@@ -105,3 +105,6 @@ dist
# config files # config files
config.json config.json
# file where we store pdf
files/*
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
"eslint-import-resolver-typescript": "^3.5.2", "eslint-import-resolver-typescript": "^3.5.2",
"fastify": "^4.9.2", "fastify": "^4.9.2",
"mongoose": "^6.7.2", "mongoose": "^6.7.2",
"multer": "^1.4.5-lts.1",
"passport": "^0.6.0", "passport": "^0.6.0",
"passport-jwt": "^4.0.0", "passport-jwt": "^4.0.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
"@types/passport-jwt": "^3.0.8", "@types/passport-jwt": "^3.0.8",
"@types/passport-local": "^1.0.34", "@types/passport-local": "^1.0.34",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.11",
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0", "@typescript-eslint/parser": "^5.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
...@@ -2683,6 +2685,23 @@ ...@@ -2683,6 +2685,23 @@
"@nestjs/core": "^9.0.0" "@nestjs/core": "^9.0.0"
} }
}, },
"node_modules/@nestjs/platform-express/node_modules/multer": {
"version": "1.4.4-lts.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz",
"integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==",
"dependencies": {
"append-field": "^1.0.0",
"busboy": "^1.0.0",
"concat-stream": "^1.5.2",
"mkdirp": "^0.5.4",
"object-assign": "^4.1.1",
"type-is": "^1.6.4",
"xtend": "^4.0.0"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/@nestjs/schematics": { "node_modules/@nestjs/schematics": {
"version": "9.0.3", "version": "9.0.3",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz",
...@@ -3232,6 +3251,12 @@ ...@@ -3232,6 +3251,12 @@
"@types/superagent": "*" "@types/superagent": "*"
} }
}, },
"node_modules/@types/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==",
"dev": true
},
"node_modules/@types/webidl-conversions": { "node_modules/@types/webidl-conversions": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
...@@ -8255,9 +8280,9 @@ ...@@ -8255,9 +8280,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/multer": { "node_modules/multer": {
"version": "1.4.4-lts.1", "version": "1.4.5-lts.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
"integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
"dependencies": { "dependencies": {
"append-field": "^1.0.0", "append-field": "^1.0.0",
"busboy": "^1.0.0", "busboy": "^1.0.0",
...@@ -12979,6 +13004,22 @@ ...@@ -12979,6 +13004,22 @@
"express": "4.18.2", "express": "4.18.2",
"multer": "1.4.4-lts.1", "multer": "1.4.4-lts.1",
"tslib": "2.4.1" "tslib": "2.4.1"
},
"dependencies": {
"multer": {
"version": "1.4.4-lts.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz",
"integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==",
"requires": {
"append-field": "^1.0.0",
"busboy": "^1.0.0",
"concat-stream": "^1.5.2",
"mkdirp": "^0.5.4",
"object-assign": "^4.1.1",
"type-is": "^1.6.4",
"xtend": "^4.0.0"
}
}
} }
}, },
"@nestjs/schematics": { "@nestjs/schematics": {
...@@ -13468,6 +13509,12 @@ ...@@ -13468,6 +13509,12 @@
"@types/superagent": "*" "@types/superagent": "*"
} }
}, },
"@types/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==",
"dev": true
},
"@types/webidl-conversions": { "@types/webidl-conversions": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
...@@ -17223,9 +17270,9 @@ ...@@ -17223,9 +17270,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"multer": { "multer": {
"version": "1.4.4-lts.1", "version": "1.4.5-lts.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
"integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
"requires": { "requires": {
"append-field": "^1.0.0", "append-field": "^1.0.0",
"busboy": "^1.0.0", "busboy": "^1.0.0",
......
...@@ -5,12 +5,14 @@ import { PeopleModule } from './people/people.module'; ...@@ -5,12 +5,14 @@ import { PeopleModule } from './people/people.module';
import { GroupsModule } from './groups/groups.module'; import { GroupsModule } from './groups/groups.module';
import { LoginModule } from './login/login.module'; import { LoginModule } from './login/login.module';
import { InternshipsModule } from './internships/internships.module'; import { InternshipsModule } from './internships/internships.module';
import { ResourcesModule } from './resources/resources.module';
@Module({ @Module({
imports: [ imports: [
PeopleModule, PeopleModule,
GroupsModule, GroupsModule,
InternshipsModule, InternshipsModule,
ResourcesModule,
MongooseModule.forRoot(config.mongodb.uri), MongooseModule.forRoot(config.mongodb.uri),
LoginModule LoginModule
], ],
......
export interface IServerConfig { interface IServerConfig {
uri: string;
port: number; port: number;
} }
export interface IMongodbConfig { interface IResources {
root: string;
agreements: string;
}
interface IMongodbConfig {
uri: string; uri: string;
} }
export interface IConfig { export interface IConfig {
server: IServerConfig; server: IServerConfig;
resources: IResources;
mongodb: IMongodbConfig; mongodb: IMongodbConfig;
} }
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
"uri": "localhost", "uri": "localhost",
"port": 3001 "port": 3001
}, },
"resources": {
"internshipAgreements": "internship-agreements"
},
"mongodb": { "mongodb": {
"uri": "mongodb://localhost:27017/internship-manager" "uri": "mongodb://localhost:27017/internship-manager"
} }
......
{ {
"server": { "server": {
"uri": "localhost",
"port": 3001 "port": 3001
}, },
"resources": {
"internshipAgreements": "internship-agreements"
},
"mongodb": { "mongodb": {
"uri": "mongodb://localhost:27017/internship-manager" "uri": "mongodb://localhost:27017/internship-manager"
} }
......
...@@ -7,7 +7,8 @@ import { exit } from 'process'; ...@@ -7,7 +7,8 @@ import { exit } from 'process';
const CONFIG_DEV = 'config.json'; const CONFIG_DEV = 'config.json';
const CONFIG_PROD = 'config.prod.json'; const CONFIG_PROD = 'config.prod.json';
let config; let config: IConfig;
console.log(process.env.NODE_ENV);
// Load config based on env // Load config based on env
switch (process.env.NODE_ENV) { switch (process.env.NODE_ENV) {
case 'dev': case 'dev':
...@@ -30,5 +31,7 @@ switch (process.env.NODE_ENV) { ...@@ -30,5 +31,7 @@ switch (process.env.NODE_ENV) {
exit(-1); exit(-1);
} }
console.log(config);
// Export config // Export config
export default config; export default config;
...@@ -120,8 +120,9 @@ export class InternshipDao { ...@@ -120,8 +120,9 @@ export class InternshipDao {
content: string | boolean, content: string | boolean,
): Promise<Internship | void> => ): Promise<Internship | void> =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
console.log('%s/%s: {%s}', studentId, state, content);
if (!isStateValid(state)) reject(BAD_REQUEST); if (!isStateValid(state)) reject(BAD_REQUEST);
console.log(typeof content); console.log(content);
let nextState: string, contentHolder: string; let nextState: string, contentHolder: string;
let valid = false; let valid = false;
switch (state) { switch (state) {
......
...@@ -7,13 +7,23 @@ import { ...@@ -7,13 +7,23 @@ import {
Param, Param,
Body, Body,
UseInterceptors, UseInterceptors,
UploadedFile,
} from '@nestjs/common'; } from '@nestjs/common';
import { BAD_TRACKING_STATE } from 'src/shared/HttpError'; import { BAD_REQUEST, BAD_TRACKING_STATE } from 'src/shared/HttpError';
import * as InternshipStates from 'src/shared/InternshipState'; import * as InternshipStates from 'src/shared/InternshipState';
import { HttpInterceptor } from '../interceptors/http.interceptor'; import { HttpInterceptor } from '../interceptors/http.interceptor';
import { CreateInternshipDto } from './dto/create-internship.dto'; import { CreateInternshipDto } from './dto/create-internship.dto';
import { InternshipEntity } from './entities/internship.entity'; import { InternshipEntity } from './entities/internship.entity';
import { InternshipService } from './internships.service'; import { InternshipService } from './internships.service';
import { FileInterceptor } from '@nestjs/platform-express';
import {
STATE_RESPONSIBLE_ACCEPTS_INTERNSHIP_INFORMATION,
STATE_STUDENT_ENTERS_INTERNSHIP_INFORMATION,
} from 'src/shared/InternshipState';
import config from 'src/config';
import { Optional } from '@nestjs/common/decorators';
import { v4 } from 'uuid';
import { diskStorage } from 'multer';
@Controller('internships') @Controller('internships')
@UseInterceptors(HttpInterceptor) @UseInterceptors(HttpInterceptor)
...@@ -47,18 +57,43 @@ export class InternshipsController { ...@@ -47,18 +57,43 @@ export class InternshipsController {
return this._internshipsService.update(params.studentId, internshipDto); return this._internshipsService.update(params.studentId, internshipDto);
} }
@Put(':studentId/tracking') // uploads even if invalid state...
@Put(':studentId/:state')
@UseInterceptors(
FileInterceptor('pdf', {
storage: diskStorage({
destination: './files',
filename: (_req, _file, cb) => {
return cb(null, `${v4()}.pdf`);
},
}),
}),
)
updateState( updateState(
@Param() params: { studentId: string }, @Param() params: { studentId: string; state: string },
@Body() body: { state: string; content?: string | boolean }, @Optional() @Body() body: { content?: boolean },
@Optional() @UploadedFile() file,
): Promise<InternshipEntity | void> { ): Promise<InternshipEntity | void> {
if (!InternshipStates.isStateValid(body.state)) if (!InternshipStates.isStateValid(params.state))
throw BAD_TRACKING_STATE(body.state); throw BAD_TRACKING_STATE(params.state);
// AMINE : Handle PDF file upload -> save file in /pdf/ folder and set content as local file URL. In case of step with no file, set content as true/false if (
params.state === STATE_STUDENT_ENTERS_INTERNSHIP_INFORMATION ||
params.state === STATE_RESPONSIBLE_ACCEPTS_INTERNSHIP_INFORMATION
) {
if (!body) throw BAD_REQUEST;
return this._internshipsService.updateTracking(
params.studentId,
params.state,
body.content,
);
}
if (!file) throw BAD_REQUEST;
console.log(params.state);
return this._internshipsService.updateTracking( return this._internshipsService.updateTracking(
params.studentId, params.studentId,
body.state, params.state,
body.content, `${config.server.uri}:${config.server.port}/resources/agreements/${file.filename}`,
); );
} }
......
import config from './config';
import { ValidationPipe } from '@nestjs/common'; import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import config from './config';
async function bootstrap() { async function bootstrap() {
const env = process.env.NODE_ENV; const env = process.env.NODE_ENV;
......
import {
Controller,
Get,
Param,
UseInterceptors,
Res,
StreamableFile,
} from '@nestjs/common';
import { NOT_FOUND } from 'src/shared/HttpError';
import { HttpInterceptor } from '../interceptors/http.interceptor';
import { Response } from 'express';
import { createReadStream, existsSync } from 'fs';
@Controller('resources')
@UseInterceptors(HttpInterceptor)
export class ResourcesController {
@Get('agreements/:filename')
serveAgreement(
@Param('filename') filename,
@Res({ passthrough: true }) res: Response,
): StreamableFile {
const filepath = `files\\${filename}`;
if (!existsSync(filepath)) throw NOT_FOUND;
const file = createReadStream(filepath);
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="agreement.pdf"',
});
return new StreamableFile(file);
}
}
import { Module, Logger } from '@nestjs/common';
import { ResourcesController } from './resources.controller';
@Module({
controllers: [ResourcesController],
providers: [Logger],
})
export class ResourcesModule {}
...@@ -11,6 +11,7 @@ export const NOT_FOUND = new NotFoundException(); ...@@ -11,6 +11,7 @@ export const NOT_FOUND = new NotFoundException();
export const CONFLICT = new ConflictException(); export const CONFLICT = new ConflictException();
export const BAD_REQUEST = new BadRequestException(); export const BAD_REQUEST = new BadRequestException();
export const INTERNAL = new InternalServerErrorException(); export const INTERNAL = new InternalServerErrorException();
export const UNPROCESSABLE_ENTITY = new UnprocessableEntityException();
export const BAD_TRACKING_STATE = (badState: string) => export const BAD_TRACKING_STATE = (badState: string) =>
new UnprocessableEntityException(`Unknown state [${badState}]`); new UnprocessableEntityException(`Unknown state [${badState}]`);
export const CUSTOM = (reason: string, errorStatus: number) => export const CUSTOM = (reason: string, errorStatus: number) =>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment