From 23d1e3b9663e038ed8faa87b17c98a481e80d573 Mon Sep 17 00:00:00 2001 From: Federico Parodi Date: Mon, 10 Oct 2022 19:23:05 -0300 Subject: [PATCH 1/3] feat: mockdata and kinto interface --- .gitignore | 3 +- src/files/files.controller.ts | 47 ++++++++++++++------ src/files/files.interfaces.ts | 7 +++ src/files/files.routes.config.ts | 16 +++++-- src/files/mock/files.ts | 74 ++++++++++++++++++++++++++++++++ src/ipfs/ipfs.service.ts | 11 ++++- 6 files changed, 139 insertions(+), 19 deletions(-) create mode 100644 src/files/files.interfaces.ts create mode 100644 src/files/mock/files.ts diff --git a/.gitignore b/.gitignore index 9b09a5f..1f7a204 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ node_modules/ -.env \ No newline at end of file +.env +.vscode/settings.json diff --git a/src/files/files.controller.ts b/src/files/files.controller.ts index 962c993..8256e79 100644 --- a/src/files/files.controller.ts +++ b/src/files/files.controller.ts @@ -1,7 +1,8 @@ import {Response, Request} from 'express'; import fileUpload from 'express-fileupload'; -import { StorageOperationController } from '../hyperledger/StorageOperationController'; +import {StorageOperationController} from '../hyperledger/StorageOperationController'; import ipfsService from '../ipfs/ipfs.service'; +import {mockListOfFiles, mockSingleFile, mocksNewFileList} from './mock/files'; class FilesController { private storageOperationController: StorageOperationController; @@ -9,33 +10,46 @@ class FilesController { this.storageOperationController = new StorageOperationController(); } - async createFile(req: Request, res: Response) { try { console.log(`[INFO] Create file for user with id ${req.params.userId}`); + if (!req.files) { + console.log(`[ERROR] Missing file`); + return res.status(500).send('Missing file'); + } const file = req.files?.file; - if (!file) res.status(400).send('Missing file'); - - //TODO Add filehash - let fileHash = "TODO_FILEHASH" - this.storageOperationController.createFileOperation(req.params.userId, fileHash, "WRITE"); + console.log(file); + + // return res.status(200).send(mocksNewFileList); + // TODO Add filehash + let fileHash = 'TODO_FILEHASH'; + this.storageOperationController.createFileOperation( + req.params.userId, + fileHash, + 'WRITE' + ); const result = await ipfsService.createFile(req.params.userId, file); res.status(200).send(result); } catch (error) { console.log(JSON.stringify(error)); - res.status(500).send('Something went wrong on IPFS createFile'); + return res.status(500).send('Something went wrong on IPFS createFile'); } } async listFiles(req: Request, res: Response) { // @TODO: Implement user authentication. console.log(`Listing files for userId ${req.params.userId}`); + // res.status(200).send(mockListOfFiles); await ipfsService .listFiles() .then(files => { //TODO IMPLEMEHTT THIS THING - this.storageOperationController.createFileOperation(req.params.userId, req.params.userId, "READ"); + this.storageOperationController.createFileOperation( + req.params.userId, + req.params.userId, + 'READ' + ); res.status(200).send(files); }) .catch(err => { @@ -48,10 +62,15 @@ class FilesController { console.log(`Download file with id ${req.params.fileId}`); const fileCID = req.params.fileId; if (!fileCID) res.status(400).send('File CID must be set!'); + // res.status(200).send(mockSingleFile); await ipfsService .readFile(fileCID) .then(file => { - this.storageOperationController.createFileOperation(req.params.userId, fileCID, "READ"); + this.storageOperationController.createFileOperation( + req.params.userId, + fileCID, + 'READ' + ); res.status(200).send(file); }) .catch(err => { @@ -68,8 +87,12 @@ class FilesController { .then(message => { //TODO RESOLVE THIS const fileCID = fileName; - this.storageOperationController.createFileOperation(req.params.userId, fileCID, "DELETE"); - res.status(200).send(message) + this.storageOperationController.createFileOperation( + req.params.userId, + fileCID, + 'DELETE' + ); + res.status(200).send(message); }) .catch(err => { console.log(JSON.stringify(err)); diff --git a/src/files/files.interfaces.ts b/src/files/files.interfaces.ts new file mode 100644 index 0000000..60eb606 --- /dev/null +++ b/src/files/files.interfaces.ts @@ -0,0 +1,7 @@ +import {MFSEntry} from 'ipfs-core-types/src/files'; + +export interface KFSEntry extends MFSEntry { + // Kinto File System Entry + id: string; + txHistory?: Array; +} diff --git a/src/files/files.routes.config.ts b/src/files/files.routes.config.ts index 0302638..457d218 100644 --- a/src/files/files.routes.config.ts +++ b/src/files/files.routes.config.ts @@ -9,12 +9,16 @@ export class FilesRoutes extends CommonRoutesConfig { } configureRoutes() { this.app + // Logic for folders .route(`/${USERS}/:userId/${FILES}`) .all((req: Request, res: Response, next: NextFunction) => { // Middleware executed on every route. @TODO: Validation @TODO: User authentication @TODO: Register on Hyperledger - console.debug("[Hyperledeger] Authenticated user: ", req.params.userId); + console.debug('[Hyperledeger] Authenticated user: ', req.params.userId); - console.debug("[Hyperledeger] Generated transaction: ", Math.random().toString(16).substr(16)); + console.debug( + '[Hyperledeger] Generated transaction: ', + Math.random().toString(16).substr(16) + ); next(); }) .get(filesController.listFiles) @@ -24,11 +28,15 @@ export class FilesRoutes extends CommonRoutesConfig { .route(`/${USERS}/:userId/${FILES}/:fileId`) .all((req: Request, res: Response, next: NextFunction) => { // Middleware executed on every route. @TODO: Validation @TODO: User authentication @TODO: Register on Hyperledger - console.debug("[Hyperledeger] Authenticated user: ", req.params.userId); + console.debug('[Hyperledeger] Authenticated user: ', req.params.userId); - console.debug("[Hyperledeger] Generated transaction: ", Math.random().toString(16).substr(16)); + console.debug( + '[Hyperledeger] Generated transaction: ', + Math.random().toString(16).substr(16) + ); next(); }) + // @TODO: Logic for folders .get(filesController.getFile) // @TODO: Define if possible .patch((req: Request, res: Response) => { diff --git a/src/files/mock/files.ts b/src/files/mock/files.ts new file mode 100644 index 0000000..4590460 --- /dev/null +++ b/src/files/mock/files.ts @@ -0,0 +1,74 @@ +import {CID} from 'multiformats/cid'; +import {KFSEntry} from '../files.interfaces'; + +export const mockListOfFiles: Array = [ + { + name: 'Picture.jpg', + type: 'file', + size: 39393993, + cid: CID.parse( + 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqasf3oclgtqy55fbzdi' + ), + id: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqasf3oclgtqy55fbzdi', + }, + { + name: 'movie.mov', + type: 'file', + size: 123123, + cid: CID.parse( + 'bafybeigdyrzt5sfp7udm7hu76uh7y26df3efuylqabf3oclgtqy55fbzdi' + ), + id: 'bafybeigdyrzt5sfp7udm7hu76uh7y26df3efuylqabf3oclgtqy55fbzdi', + }, + { + name: 'file.doc', + type: 'file', + size: 448389289, + cid: CID.parse( + 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3eduylqabf3oclgtqy55fbzdi' + ), + id: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3eduylqabf3oclgtqy55fbzdi', + }, + { + name: 'excel.xslx', + type: 'file', + size: 123123123, + cid: CID.parse( + 'bafybeigdyrzt5sfp7udm7hu76uh7y26nfrefuylqabf3oclgtqy55fbzdi' + ), + id: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nfrefuylqabf3oclgtqy55fbzdi', + }, + { + name: 'printer.pdf', + type: 'file', + size: 123123123154, + cid: CID.parse( + 'bafybeigdyrzt5sfp7udm7hu76uh7y26nv3efuylqabf3oclgtqy55fbzdi' + ), + id: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nv3efuylqabf3oclgtqy55fbzdi', + }, + { + name: 'MyFolder', + type: 'directory', + size: 8, + cid: CID.parse( + 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3qfuylqabf3oclgtqy55fbzdi' + ), + id: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3qfuylqabf3oclgtqy55fbzdi', + }, +]; + +export const mockSingleFile = mockListOfFiles[0]; + +export const mocksNewFileList = [ + ...mockListOfFiles, + { + name: 'Extrafile.png', + type: 'file', + size: 12312312345, + cid: CID.parse( + 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efualqabf3oclgtqy55fbzdi' + ), + id: 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efualqabf3oclgtqy55fbzdi', + }, +]; diff --git a/src/ipfs/ipfs.service.ts b/src/ipfs/ipfs.service.ts index 3f55ea1..102ae85 100644 --- a/src/ipfs/ipfs.service.ts +++ b/src/ipfs/ipfs.service.ts @@ -2,6 +2,7 @@ import {UploadedFile} from 'express-fileupload'; import {MFSEntry} from 'ipfs-core-types/src/files'; import {create, IPFSHTTPClient} from 'ipfs-http-client'; import all from 'it-all'; +import {KFSEntry} from '../files/files.interfaces'; /** * @TODO: Better error handling */ @@ -47,8 +48,10 @@ class IPFSService { * @returns Array w/ the results from the node * @TODO: will have to change once userId is implemented */ - public async listFiles(dir = '/'): Promise { - return await all(IPFSService.ipfsHttpClient.files.ls(dir)); + public async listFiles(dir = '/'): Promise { + return await ( + await all(IPFSService.ipfsHttpClient.files.ls(dir)) + ).map(file => this.ipfsToKinto(file)); } /** @@ -97,6 +100,10 @@ class IPFSService { throw error; }); } + + private ipfsToKinto(file: MFSEntry) { + return {...file, id: file.cid.toString()}; + } } export default new IPFSService(); From fd1a851d38f3423ffa740f559d23ceb9e47cf9a1 Mon Sep 17 00:00:00 2001 From: Federico Parodi Date: Mon, 10 Oct 2022 19:41:18 -0300 Subject: [PATCH 2/3] feat: use KFS interface --- src/files/files.controller.ts | 14 +++++++------- src/ipfs/ipfs.service.ts | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/files/files.controller.ts b/src/files/files.controller.ts index fc47b81..04de24f 100644 --- a/src/files/files.controller.ts +++ b/src/files/files.controller.ts @@ -1,7 +1,7 @@ import fileUpload from 'express-fileupload'; import {StorageOperationController} from '../hyperledger/StorageOperationController'; import ipfsService from '../ipfs/ipfs.service'; -import {MFSEntry} from 'ipfs-core-types/src/files'; +import {KFSEntry} from './files.interfaces'; class FilesController { async createFile(userId: string, file: fileUpload.UploadedFile) { @@ -11,13 +11,13 @@ class FilesController { try { return await ipfsService .createFile(userId, file) - .then((files: MFSEntry[]) => { + .then((files: KFSEntry[]) => { console.log('[DEBUG] files.controller - createFile: files', files); - files.forEach((file: MFSEntry) => { + files.forEach((file: KFSEntry) => { new StorageOperationController().createFileOperation( userId, - file.cid.toString(), + file.id, 'WRITE' ); }); @@ -36,13 +36,13 @@ class FilesController { `[INFO] files.controller - listFiles: Listing files for userId ${userId}` ); try { - return await ipfsService.listFiles().then((files: MFSEntry[]) => { + return await ipfsService.listFiles().then((files: KFSEntry[]) => { console.log('[DEBUG] files.controller - listFiles: files', files); - files.forEach((file: MFSEntry) => { + files.forEach((file: KFSEntry) => { new StorageOperationController().createFileOperation( userId, - file.cid.toString(), + file.id, 'READ' ); }); diff --git a/src/ipfs/ipfs.service.ts b/src/ipfs/ipfs.service.ts index cfac674..fc1e26c 100644 --- a/src/ipfs/ipfs.service.ts +++ b/src/ipfs/ipfs.service.ts @@ -34,7 +34,7 @@ class IPFSService { public async createFile( userId: string, file: UploadedFile - ): Promise { + ): Promise { const filePath = `/${file.name}`; console.log('[DEBUG] IPFSService - createFile', userId, file); @@ -83,7 +83,7 @@ class IPFSService { * @returns */ public async updateFile(file: File) { - const result: Array = []; + const result: Array = []; // IPFSService.ipfsHttpClient.files.write('/' + file.name, file); // result.push(...(await all(IPFSService.ipfsHttpClient.files.ls(file.name)))); return result; From c61c3a898b96579ed8a6f4494fb463e1747fd836 Mon Sep 17 00:00:00 2001 From: Federico Parodi Date: Mon, 10 Oct 2022 21:06:19 -0300 Subject: [PATCH 3/3] feat: single file Download --- src/files/files.routes.config.ts | 12 +++++++----- src/hyperledger/StorageOperationController.ts | 1 - tsconfig.json | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/files/files.routes.config.ts b/src/files/files.routes.config.ts index a43a1f5..7a63c4a 100644 --- a/src/files/files.routes.config.ts +++ b/src/files/files.routes.config.ts @@ -58,12 +58,12 @@ export class FilesRoutes extends CommonRoutesConfig { .get((req: Request, res: Response) => { // @TODO: Logic for folders const fileCID = req.params.fileId; - if (!fileCID) res.status(400).send('File CID must be set!'); + if (!fileCID) return res.status(400).send('File CID must be set!'); - const userId = req.body.userId; - if (!userId) res.status(400).send('Missing userId'); + const userId = req.params.userId; + if (!userId) return res.status(400).send('Missing userId'); - filesController + return filesController .getFile(userId, fileCID) .then(file => res.status(200).send(file)) .catch(error => @@ -72,7 +72,9 @@ export class FilesRoutes extends CommonRoutesConfig { }) // @TODO: Define if possible .patch((req: Request, res: Response) => { - res.status(200).send(`TODO: PATCH file for id ${req.params.fileId}`); + return res + .status(200) + .send(`TODO: PATCH file for id ${req.params.fileId}`); }) // @TODO: this uses filename and it's not really usable in a multiple-node context. // @TODO: will make more sense for it to use :fileId BUT you can't delete files with the CID... not really sure why diff --git a/src/hyperledger/StorageOperationController.ts b/src/hyperledger/StorageOperationController.ts index 4c22ebc..824da37 100644 --- a/src/hyperledger/StorageOperationController.ts +++ b/src/hyperledger/StorageOperationController.ts @@ -1,6 +1,5 @@ import {HyperledgerController} from './HyperledgerController'; import {StorageOperation} from './contracts/StorageOperation.interface'; -import { StorageController } from '../StorageController'; export class StorageOperationController { //TODO we can use this to show / check history diff --git a/tsconfig.json b/tsconfig.json index a213bb6..9781266 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,7 +33,7 @@ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - "types": [], /* Specify type package names to be included without being referenced in a source file. */ + "types": ["jest"], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "resolveJsonModule": true, /* Enable importing .json files. */