diff --git a/.vscode/launch.json b/.vscode/launch.json index 4878666cf0..6eba42c81b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,9 @@ "-r", "tsconfig-paths/register", "${workspaceFolder}/src/main.ts", - "sync" + "get", + "item", + "f6709d4f-69c3-4957-93e4-a82d01259693" ], "env": { "NODE_ENV": "debug" diff --git a/src/commands/get.command.ts b/src/commands/get.command.ts index 346c689ebd..16d0a407f5 100644 --- a/src/commands/get.command.ts +++ b/src/commands/get.command.ts @@ -7,87 +7,85 @@ import { CollectionService } from 'jslib/abstractions/collection.service'; import { FolderService } from 'jslib/abstractions/folder.service'; import { TotpService } from 'jslib/abstractions/totp.service'; +import { Response } from '../models/response'; +import { CipherResponse } from '../models/response/cipherResponse'; +import { CollectionResponse } from '../models/response/collectionResponse'; +import { FolderResponse } from '../models/response/folderResponse'; +import { StringResponse } from '../models/response/stringResponse'; + export class GetCommand { constructor(private cipherService: CipherService, private folderService: FolderService, private collectionService: CollectionService, private totpService: TotpService) { } - async run(object: string, id: string, cmd: program.Command) { + async run(object: string, id: string, cmd: program.Command): Promise { switch (object) { case 'item': - await this.getCipher(id); - break; + return await this.getCipher(id); case 'totp': - await this.getTotp(id); - break; + return await this.getTotp(id); case 'folder': - await this.getFolder(id); - break; + return await this.getFolder(id); case 'collection': - await this.getCollection(id); - break; + return await this.getCollection(id); default: - console.log('Unknown object: ' + object); - break; + return Response.badRequest('Unknown object.'); } } private async getCipher(id: string) { const cipher = await this.cipherService.get(id); if (cipher == null) { - console.log('Not found.'); - return; + return Response.notFound(); } const decCipher = await cipher.decrypt(); - console.log(JSON.stringify(decCipher)); + const res = new CipherResponse(decCipher); + return Response.success(res); } private async getTotp(id: string) { const cipher = await this.cipherService.get(id); if (cipher == null) { - console.log('Not found.'); - return; + return Response.notFound(); } if (cipher.type !== CipherType.Login) { - console.log('Not a login.'); - return; + return Response.badRequest('Not a login.'); } const decCipher = await cipher.decrypt(); if (decCipher.login.totp == null || decCipher.login.totp === '') { - console.log('No TOTP available.'); - return; + return Response.error('No TOTP available for this login.'); } const totp = await this.totpService.getCode(decCipher.login.totp); if (totp == null) { - console.log('Couldn\'t generate TOTP code.'); - return; + return Response.error('Couldn\'t generate TOTP code.'); } - console.log(JSON.stringify(totp)); + const res = new StringResponse(totp); + return Response.success(res); } private async getFolder(id: string) { const folder = await this.folderService.get(id); if (folder == null) { - console.log('Not found.'); - return; + return Response.notFound(); } const decFolder = await folder.decrypt(); - console.log(JSON.stringify(decFolder)); + const res = new FolderResponse(decFolder); + return Response.success(res); } private async getCollection(id: string) { const collection = await this.collectionService.get(id); if (collection == null) { - console.log('Not found.'); - return; + return Response.notFound(); } const decCollection = await collection.decrypt(); - console.log(JSON.stringify(decCollection)); + const res = new CollectionResponse(decCollection); + return Response.success(res); } } diff --git a/src/commands/list.command.ts b/src/commands/list.command.ts index 13e94b2bc0..cdb25b0645 100644 --- a/src/commands/list.command.ts +++ b/src/commands/list.command.ts @@ -4,39 +4,44 @@ import { CipherService } from 'jslib/abstractions/cipher.service'; import { CollectionService } from 'jslib/services/collection.service'; import { FolderService } from 'jslib/services/folder.service'; +import { Response } from '../models/response'; +import { CipherResponse } from '../models/response/cipherResponse'; +import { CollectionResponse } from '../models/response/collectionResponse'; +import { FolderResponse } from '../models/response/folderResponse'; +import { ListResponse } from '../models/response/listResponse'; + export class ListCommand { constructor(private cipherService: CipherService, private folderService: FolderService, private collectionService: CollectionService) { } - async run(object: string, cmd: program.Command) { + async run(object: string, cmd: program.Command): Promise { switch (object) { case 'items': - await this.listCiphers(); - break; + return await this.listCiphers(); case 'folders': - await this.listFolders(); - break; + return await this.listFolders(); case 'collections': - await this.listCollections(); - break; + return await this.listCollections(); default: - console.log('Unknown object: ' + object); - break; + return Response.badRequest('Unknown object.'); } } private async listCiphers() { const ciphers = await this.cipherService.getAllDecrypted(); - console.log(JSON.stringify(ciphers)); + const res = new ListResponse(ciphers.map((o) => new CipherResponse(o))); + return Response.success(res); } private async listFolders() { const folders = await this.folderService.getAllDecrypted(); - console.log(JSON.stringify(folders)); + const res = new ListResponse(folders.map((o) => new FolderResponse(o))); + return Response.success(res); } private async listCollections() { const collections = await this.collectionService.getAllDecrypted(); - console.log(JSON.stringify(collections)); + const res = new ListResponse(collections.map((o) => new CollectionResponse(o))); + return Response.success(res); } } diff --git a/src/commands/login.command.ts b/src/commands/login.command.ts index 0107c9a088..9d132ac6f8 100644 --- a/src/commands/login.command.ts +++ b/src/commands/login.command.ts @@ -4,17 +4,18 @@ import { AuthResult } from 'jslib/models/domain/authResult'; import { AuthService } from 'jslib/abstractions/auth.service'; -export class LoginCommand { - constructor(private authService: AuthService) { +import { Response } from '../models/response'; - } +export class LoginCommand { + constructor(private authService: AuthService) { } async run(email: string, password: string, cmd: program.Command) { try { const result = await this.authService.logIn(email, password); - console.log(result); + // TODO: 2FA + return Response.success(); } catch (e) { - console.log(e); + return Response.success(e.toString()); } } } diff --git a/src/commands/sync.command.ts b/src/commands/sync.command.ts index 05eea6fce7..c7b259f50f 100644 --- a/src/commands/sync.command.ts +++ b/src/commands/sync.command.ts @@ -2,15 +2,17 @@ import * as program from 'commander'; import { SyncService } from 'jslib/abstractions/sync.service'; +import { Response } from '../models/response'; + export class SyncCommand { constructor(private syncService: SyncService) { } - async run(cmd: program.Command) { + async run(cmd: program.Command): Promise { try { const result = await this.syncService.fullSync(false); - console.log(result); + return Response.success(); } catch (e) { - console.log(e); + return Response.success(e.toString()); } } } diff --git a/src/models/response.ts b/src/models/response.ts new file mode 100644 index 0000000000..57ac9e8c4e --- /dev/null +++ b/src/models/response.ts @@ -0,0 +1,30 @@ +import { BaseResponse } from './response/baseResponse'; + +export class Response { + static error(message: string): Response { + var res = new Response(); + res.success = false; + res.message = message; + return res; + } + + static notFound(): Response { + return Response.error('Not found.'); + } + + static badRequest(message: string): Response { + return Response.error(message); + } + + static success(data?: BaseResponse): Response { + var res = new Response(); + res.success = true; + res.data = data; + return res; + } + + success: boolean; + message: string; + errorCode: number; + data: BaseResponse; +} diff --git a/src/models/response/baseResponse.ts b/src/models/response/baseResponse.ts new file mode 100644 index 0000000000..51263967f6 --- /dev/null +++ b/src/models/response/baseResponse.ts @@ -0,0 +1,9 @@ +export abstract class BaseResponse { + object: string; + + constructor(object?: string) { + if (object != null) { + this.object = object; + } + } +} diff --git a/src/models/response/cipherResponse.ts b/src/models/response/cipherResponse.ts new file mode 100644 index 0000000000..ead00ce687 --- /dev/null +++ b/src/models/response/cipherResponse.ts @@ -0,0 +1,22 @@ +import { CipherView } from 'jslib/models/view/cipherView'; + +import { BaseResponse } from './baseResponse'; + +import { CipherType } from 'jslib/enums'; + +export class CipherResponse extends BaseResponse { + id: string; + organizationId: string; + type: CipherType; + name: string; + notes: string; + + constructor(o: CipherView) { + super('item'); + this.id = o.id; + this.organizationId = o.organizationId; + this.type = o.type; + this.name = o.name; + this.notes = o.notes; + } +} diff --git a/src/models/response/collectionResponse.ts b/src/models/response/collectionResponse.ts new file mode 100644 index 0000000000..03627a11e2 --- /dev/null +++ b/src/models/response/collectionResponse.ts @@ -0,0 +1,16 @@ +import { CollectionView } from 'jslib/models/view/collectionView'; + +import { BaseResponse } from './baseResponse'; + +export class CollectionResponse extends BaseResponse { + id: string; + organizationId: string; + name: string; + + constructor(o: CollectionView) { + super('collection'); + this.id = o.id; + this.organizationId = o.organizationId; + this.name = o.name; + } +} diff --git a/src/models/response/folderResponse.ts b/src/models/response/folderResponse.ts new file mode 100644 index 0000000000..6184c53fe2 --- /dev/null +++ b/src/models/response/folderResponse.ts @@ -0,0 +1,14 @@ +import { FolderView } from 'jslib/models/view/folderView'; + +import { BaseResponse } from './baseResponse'; + +export class FolderResponse extends BaseResponse { + id: string; + name: string; + + constructor(o: FolderView) { + super('folder'); + this.id = o.id; + this.name = o.name; + } +} diff --git a/src/models/response/listResponse.ts b/src/models/response/listResponse.ts new file mode 100644 index 0000000000..970f58fe07 --- /dev/null +++ b/src/models/response/listResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from './baseResponse'; + +export class ListResponse extends BaseResponse { + data: BaseResponse[]; + + constructor(data: BaseResponse[]) { + super('list'); + this.data = data; + } +} diff --git a/src/models/response/stringResponse.ts b/src/models/response/stringResponse.ts new file mode 100644 index 0000000000..4087359574 --- /dev/null +++ b/src/models/response/stringResponse.ts @@ -0,0 +1,14 @@ +import { CipherView } from 'jslib/models/view/cipherView'; + +import { BaseResponse } from './baseResponse'; + +import { CipherType } from 'jslib/enums'; + +export class StringResponse extends BaseResponse { + data: string; + + constructor(data: string) { + super('string'); + this.data = data; + } +} diff --git a/src/program.ts b/src/program.ts index 0cd9b84204..f132c46829 100644 --- a/src/program.ts +++ b/src/program.ts @@ -7,6 +7,10 @@ import { GetCommand } from './commands/get.command'; import { Main } from './main'; +import { Response } from './models/response'; +import { ListResponse } from './models/response/listResponse'; +import { StringResponse } from './models/response/stringResponse'; + export class Program { constructor(private main: Main) { } @@ -21,8 +25,8 @@ export class Program { .option('-c, --code ', '2FA code.') .action(async (email: string, password: string, cmd: program.Command) => { const command = new LoginCommand(this.main.authService); - await command.run(email, password, cmd); - process.exit(); + const response = await command.run(email, password, cmd); + this.processResponse(response); }); program @@ -39,8 +43,8 @@ export class Program { .option('-f, --force', 'Force a full sync.') .action(async (cmd) => { const command = new SyncCommand(this.main.syncService); - await command.run(cmd); - process.exit(); + const response = await command.run(cmd); + this.processResponse(response); }); program @@ -49,8 +53,8 @@ export class Program { .action(async (object, cmd) => { const command = new ListCommand(this.main.cipherService, this.main.folderService, this.main.collectionService); - await command.run(object, cmd); - process.exit(); + const response = await command.run(object, cmd); + this.processResponse(response); }); program @@ -59,8 +63,8 @@ export class Program { .action(async (object, id, cmd) => { const command = new GetCommand(this.main.cipherService, this.main.folderService, this.main.collectionService, this.main.totpService); - await command.run(object, id, cmd); - process.exit(); + const response = await command.run(object, id, cmd); + this.processResponse(response); }); program @@ -84,4 +88,22 @@ export class Program { program .parse(process.argv); } + + private processResponse(response: Response) { + if (response.success) { + if (response.data != null) { + if (response.data.object === 'string') { + console.log((response.data as StringResponse).data); + } else if (response.data.object === 'list') { + console.log(JSON.stringify((response.data as ListResponse).data)); + } else { + console.log(JSON.stringify(response.data)); + } + } + process.exit(); + } else { + console.log(response.message); + process.exit(1); + } + } }