API LoopBack 4- API com Upload e Download de ficheiros

// Copyright IBM Corp. 2018,2020. All Rights Reserved.// Node module: @loopback/example-todo// This file is licensed under the MIT License.// License text available at https://opensource.org/licenses/MITimport {AuthenticationComponent} from '@loopback/authentication';import {JWTAuthenticationComponent,SECURITY_SCHEME_SPEC,UserServiceBindings,} from '@loopback/authentication-jwt';import {DbDataSource} from './datasources';import {BootMixin} from '@loopback/boot';import {ApplicationConfig} from '@loopback/core';import {RepositoryMixin} from '@loopback/repository';import {Request, Response, RestApplication} from '@loopback/rest';import {RestExplorerBindings,RestExplorerComponent,} from '@loopback/rest-explorer';import {ServiceMixin} from '@loopback/service-proxy';import morgan from 'morgan';import path from 'path';import {MySequence} from './sequence';export {ApplicationConfig};export class TodoListApplication extends BootMixin(ServiceMixin(RepositoryMixin(RestApplication)),) {constructor(options: ApplicationConfig = {}) {super(options);// Set up the custom sequencethis.sequence(MySequence);// Set up default home pagethis.static('/', path.join(__dirname, '../public'));// Customize @loopback/rest-explorer configuration herethis.configure(RestExplorerBindings.COMPONENT).to({path: '/explorer',});this.component(RestExplorerComponent);this.projectRoot = __dirname;// Customize @loopback/boot Booter Conventions herethis.bootOptions = {controllers: {// Customize ControllerBooter Conventions heredirs: ['controllers'],extensions: ['.controller.js'],nested: true,},};// Mount authentication systemthis.component(AuthenticationComponent);// Mount jwt componentthis.component(JWTAuthenticationComponent);// Bind datasourcethis.dataSource(DbDataSource, UserServiceBindings.DATASOURCE_NAME);this.setupLogging();}private setupLogging() {// Register `morgan` express middleware// Create a middleware factory wrapper for `morgan(format, options)`const morganFactory = (config?: morgan.Options<Request, Response>) => {this.debug('Morgan configuration', config);return morgan('combined', config);};// Print out logs using `debug`const defaultConfig: morgan.Options<Request, Response> = {stream: {write: str => {this._debug(str);},},};this.expressMiddleware(morganFactory, defaultConfig, {injectConfiguration: 'watch',key: 'middleware.morgan',});}}
// Copyright IBM Corp. 2020. All Rights Reserved.// Node module: @loopback/example-todo-jwt// This file is licensed under the MIT License.// License text available at https://opensource.org/licenses/MITimport {authenticate, TokenService} from '@loopback/authentication';import {Credentials,MyUserService,TokenServiceBindings,User,UserRepository,UserServiceBindings,} from '@loopback/authentication-jwt';import {inject} from '@loopback/core';import {model, property, repository} from '@loopback/repository';import {get,getModelSchemaRef,post,requestBody,SchemaObject,} from '@loopback/rest';import {SecurityBindings, securityId, UserProfile} from '@loopback/security';import {genSalt, hash} from 'bcryptjs';import _ from 'lodash';@model()export class NewUserRequest extends User {@property({type: 'string',required: true,})password: string;}const CredentialsSchema: SchemaObject = {type: 'object',required: ['email', 'password'],properties: {email: {type: 'string',format: 'email',},password: {type: 'string',minLength: 8,},},};export const CredentialsRequestBody = {description: 'The input of login function',required: true,content: {'application/json': {schema: CredentialsSchema},},};export class UserController {constructor(@inject(TokenServiceBindings.TOKEN_SERVICE)public jwtService: TokenService,@inject(UserServiceBindings.USER_SERVICE)public userService: MyUserService,@inject(SecurityBindings.USER, {optional: true})public user: UserProfile,@repository(UserRepository) protected userRepository: UserRepository,) {}@post('/users/login', {responses: {'200': {description: 'Token',content: {'application/json': {schema: {type: 'object',properties: {token: {type: 'string',},},},},},},},})async login(@requestBody(CredentialsRequestBody) credentials: Credentials,): Promise<{token: string}> {// ensure the user exists, and the password is correctconst user = await this.userService.verifyCredentials(credentials);// convert a User object into a UserProfile object (reduced set of properties)const userProfile = this.userService.convertToUserProfile(user);// create a JSON Web Token based on the user profileconst token = await this.jwtService.generateToken(userProfile);return {token};}@authenticate('jwt')@get('/whoAmI', {responses: {'200': {description: 'Return current user',content: {'application/json': {schema: {type: 'string',},},},},},})async whoAmI(@inject(SecurityBindings.USER)currentUserProfile: UserProfile,): Promise<string> {return currentUserProfile[securityId];}@post('/signup', {responses: {'200': {description: 'User',content: {'application/json': {schema: {'x-ts-type': User,},},},},},})async signUp(@requestBody({content: {'application/json': {schema: getModelSchemaRef(NewUserRequest, {title: 'NewUser',}),},},})newUserRequest: NewUserRequest,): Promise<User> {const password = await hash(newUserRequest.password, await genSalt());const savedUser = await this.userRepository.create(_.omit(newUserRequest, 'password'),);await this.userRepository.userCredentials(savedUser.id).create({password});return savedUser;}}
// Copyright IBM Corp. 2018,2020. All Rights Reserved.// Node module: @loopback/example-todo// This file is licensed under the MIT License.// License text available at https://opensource.org/licenses/MITimport {inject} from '@loopback/core';import {Count,CountSchema,Filter,FilterExcludingWhere,repository,Where,} from '@loopback/repository';import {del,get,getModelSchemaRef,HttpErrors,param,patch,post,put,requestBody,} from '@loopback/rest';import {Todo} from '../models';import {TodoRepository} from '../repositories';import {Geocoder} from '../services';import {authenticate} from '@loopback/authentication';// ------------------------------------@authenticate('jwt') // <---- Apply the @authenticate decorator at the class levelexport class TodoController {constructor(@repository(TodoRepository)public todoRepository: TodoRepository,@inject('services.Geocoder') protected geoService: Geocoder,) {}@post('/todos', {responses: {'200': {description: 'Todo model instance',content: {'application/json': {schema: getModelSchemaRef(Todo)}},},},})async create(@requestBody({content: {'application/json': {schema: getModelSchemaRef(Todo, {title: 'NewTodo',exclude: ['id'],}),},},})todo: Omit<Todo, 'id'>,): Promise<Todo> {if (todo.remindAtAddress) {const geo = await this.geoService.geocode(todo.remindAtAddress);// ignoring because if the service is down, the following section will// not be covered/* istanbul ignore next */if (!geo[0]) {// address not foundthrow new HttpErrors.BadRequest(`Address not found: ${todo.remindAtAddress}`,);}// Encode the coordinates as "lat,lng" (Google Maps API format). See also// https://stackoverflow.com/q/7309121/69868// https://gis.stackexchange.com/q/7379todo.remindAtGeo = `${geo[0].y},${geo[0].x}`;}return this.todoRepository.create(todo);}@get('/todos/{id}', {responses: {'200': {description: 'Todo model instance',content: {'application/json': {schema: getModelSchemaRef(Todo, {includeRelations: true}),},},},},})async findById(@param.path.number('id') id: number,@param.filter(Todo, {exclude: 'where'}) filter?: FilterExcludingWhere<Todo>,): Promise<Todo> {return this.todoRepository.findById(id, filter);}@get('/todos', {responses: {'200': {description: 'Array of Todo model instances',content: {'application/json': {schema: {type: 'array',items: getModelSchemaRef(Todo, {includeRelations: true}),},},},},},})async find(@param.filter(Todo) filter?: Filter<Todo>): Promise<Todo[]> {return this.todoRepository.find(filter);}@put('/todos/{id}', {responses: {'204': {description: 'Todo PUT success',},},})async replaceById(@param.path.number('id') id: number,@requestBody() todo: Todo,): Promise<void> {await this.todoRepository.replaceById(id, todo);}@patch('/todos/{id}', {responses: {'204': {description: 'Todo PATCH success',},},})async updateById(@param.path.number('id') id: number,@requestBody({content: {'application/json': {schema: getModelSchemaRef(Todo, {partial: true}),},},})todo: Todo,): Promise<void> {await this.todoRepository.updateById(id, todo);}@del('/todos/{id}', {responses: {'204': {description: 'Todo DELETE success',},},})async deleteById(@param.path.number('id') id: number): Promise<void> {await this.todoRepository.deleteById(id);}@get('/todos/count', {responses: {'200': {description: 'Todo model count',content: {'application/json': {schema: CountSchema}},},},})async count(@param.where(Todo) where?: Where<Todo>): Promise<Count> {return this.todoRepository.count(where);}@patch('/todos', {responses: {'200': {description: 'Todo PATCH success count',content: {'application/json': {schema: CountSchema}},},},})async updateAll(@requestBody({content: {'application/json': {schema: getModelSchemaRef(Todo, {partial: true}),},},})todo: Todo,@param.where(Todo) where?: Where<Todo>,): Promise<Count> {return this.todoRepository.updateAll(todo, where);}}
constructor(@inject(FILE_UPLOAD_SERVICE) private handler: FileUploadHandler,) {}@post('/files', {responses: {200: {content: {'application/json': {schema: {type: 'object',},},},description: 'Files and fields',},},})async fileUpload(@requestBody.file()request: Request,@inject(RestBindings.Http.RESPONSE) response: Response,): Promise<object> {return new Promise<object>((resolve, reject) => {this.handler(request, response, (err: unknown) => {if (err) reject(err);else {resolve(FileUploadController.getFilesAndFields(request));}});});}/*** Get files and fields for the request* @param request - Http request*/private static getFilesAndFields(request: Request) {const uploadedFiles = request.files;const mapper = (f: globalThis.Express.Multer.File) => ({fieldname: f.fieldname,originalname: f.originalname,encoding: f.encoding,mimetype: f.mimetype,size: f.size,});let files: object[] = [];if (Array.isArray(uploadedFiles)) {files = uploadedFiles.map(mapper);} else {for (const filename in uploadedFiles) {files.push(...uploadedFiles[filename].map(mapper));}}return {files, fields: request.body};}}
<!DOCTYPE html><html lang="en"><head><title>File upload and download</title><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1" /><linkrel="shortcut icon"type="image/x-icon"href="https://loopback.io/favicon.ico"/><style>h3 {margin-left: 25px;text-align: center;}a,a:visited {color: #3f5dff;}h3 a {margin-left: 10px;}a:hover,a:focus,a:active {color: #001956;}.power {position: absolute;bottom: 25px;left: 50%;transform: translateX(-50%);}.info {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}.info h1 {text-align: center;margin-bottom: 0;}.info p {text-align: center;margin-bottom: 3em;margin-top: 1em;}</style><script>/*** Submit the upload form*/function setupUploadForm() {const formElem = document.getElementById('uploadForm');formElem.onsubmit = async e => {e.preventDefault();const res = await fetch('/files', {method: 'POST',body: new FormData(formElem),});const body = await res.json();console.log('Response from upload', body);await fetchFiles();};}/*** List uploaded files*/async function fetchFiles() {const res = await fetch('/files');const files = await res.json();console.log('Response from list', files);const list = files.map(f => `<li><a href="/files/${f}">${f}</a></li>\n`,);document.getElementById('fileList').innerHTML = list.join('');}async function init() {setupUploadForm();await fetchFiles();}</script></head><body onload="init();"><div class="info"><h1>File upload and download</h1><div id="upload"><h3>Upload files</h3><form id="uploadForm"><label for="files">Select files:</label><input type="file" id="files" name="files" multiple /><br /><br /><label for="note">Note:</label><inputtype="text"name="note"id="note"placeholder="Note about the files"/><br /><br /><input type="submit" /></form></div><div id="download"><h3>Download files</h3><ul id="fileList"></ul><button onclick="fetchFiles()">Refresh</button></div><h3>OpenAPI spec: <a href="/openapi.json">/openapi.json</a></h3><h3>API Explorer: <a href="/explorer">/explorer</a></h3></div><footer class="power"><a href="https://loopback.io" target="_blank"><imgsrc="https://loopback.io/images/branding/powered-by-loopback/blue/powered-by-loopback-sm.png"/></a></footer></body></html>

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
João Saraiva

João Saraiva

More from Medium

Top Mobile App Development Trends for 2022 | Clarika

Art Toy Design: Galactic Rat In Progress

WHY WHITESPACE IS SO IMPORTANT IN WEB DESIGN?

Project 3 — Object Rendering in Rhino