From 01efaa5e768dbc08376761696b77cd9671023e06 Mon Sep 17 00:00:00 2001 From: Chris Hines <chris.hines@monash.edu> Date: Thu, 10 Jan 2019 14:07:12 +1100 Subject: [PATCH] all sorts of changes --- e2e/app/authorisation.service.spec.ts | 15 + e2e/app/authorisation.service.ts | 9 + package-lock.json | 370 +++++++++++++----- package.json | 31 +- src/app/app-routing.module.ts | 8 +- src/app/app.component.css | 11 +- src/app/app.component.html | 7 +- src/app/app.component.ts | 29 +- src/app/app.module.ts | 24 +- src/app/authorisation.service.ts | 154 +++++++- src/app/batchinterface.ts | 1 - src/app/computesite.ts | 4 + src/app/computesites.service.ts | 96 ++++- .../connecting.component.css} | 0 src/app/connecting/connecting.component.html | 11 + .../connecting/connecting.component.spec.ts | 25 ++ src/app/connecting/connecting.component.ts | 73 ++++ src/app/file-explorer/CREDIT | 1 + .../file-explorer/file-explorer.component.css | 39 ++ .../file-explorer.component.html | 139 +++++++ .../file-explorer/file-explorer.component.ts | 109 ++++++ src/app/file-explorer/file-explorer.module.ts | 48 +++ .../newFolderDialog.component.css | 0 .../newFolderDialog.component.html | 12 + .../newFolderDialog.component.spec.ts | 28 ++ .../newFolderDialog.component.ts | 15 + .../renameDialog/renameDialog.component.css | 0 .../renameDialog/renameDialog.component.html | 12 + .../renameDialog/renameDialog.component.ts | 15 + src/app/file-explorer/model/element.ts | 6 + src/app/identity.ts | 4 + src/app/job.ts | 5 +- src/app/job/job.component.html | 15 + src/app/job/job.component.ts | 8 +- src/app/joblist/joblist.component.html | 6 +- src/app/joblist/joblist.component.ts | 43 +- src/app/keygen/keygen.component.css | 0 src/app/keygen/keygen.component.html | 11 + .../keygen.component.spec.ts} | 12 +- src/app/keygen/keygen.component.ts | 46 +++ src/app/launcher/launcher.component.css | 11 +- src/app/launcher/launcher.component.html | 38 +- src/app/launcher/launcher.component.ts | 53 ++- .../logoutdialog/logoutdialog.component.ts | 6 +- src/app/modaldialog/modaldialog.component.css | 0 .../modaldialog/modaldialog.component.html | 10 + .../modaldialog/modaldialog.component.spec.ts | 25 ++ src/app/modaldialog/modaldialog.component.ts | 20 + .../shareconnect/shareconnect.component.css | 3 + .../shareconnect/shareconnect.component.html | 38 ++ .../shareconnect.component.spec.ts | 25 ++ .../shareconnect/shareconnect.component.ts | 71 ++++ src/app/strudelapp.ts | 5 + .../strudelapplist.component.css | 0 .../strudelapplist.component.html | 16 + .../strudelapplist.component.spec.ts | 25 ++ .../strudelapplist.component.ts | 38 ++ src/app/submit-app.service.spec.ts | 15 + src/app/submit-app.service.ts | 158 ++++++++ .../sv2-side-nav/sv2-side-nav.component.css | 0 .../sv2-side-nav/sv2-side-nav.component.html | 16 + .../sv2-side-nav.component.spec.ts | 25 ++ .../sv2-side-nav/sv2-side-nav.component.ts | 31 ++ src/app/tes.service.ts | 336 +++++++++------- src/app/teserrors/teserrors.component.html | 3 - src/app/teserrors/teserrors.component.ts | 31 -- src/app/transfer/transfer.component.css | 3 + src/app/transfer/transfer.component.html | 54 +++ src/app/transfer/transfer.component.spec.ts | 25 ++ src/app/transfer/transfer.component.ts | 175 +++++++++ src/assets/config/authservers.json | 34 ++ src/assets/config/computesites.json | 40 ++ src/assets/config/computesites.json~ | 26 ++ src/assets/config/cvluwaapps.json | 62 +++ src/assets/config/m3apps.json | 62 +++ src/assets/config/m3staticapps.json | 10 + src/environments/environment.prod.ts | 4 +- src/environments/environment.ts | 3 +- src/index.html | 4 +- 79 files changed, 2521 insertions(+), 422 deletions(-) create mode 100644 e2e/app/authorisation.service.spec.ts create mode 100644 e2e/app/authorisation.service.ts rename src/app/{teserrors/teserrors.component.css => connecting/connecting.component.css} (100%) create mode 100644 src/app/connecting/connecting.component.html create mode 100644 src/app/connecting/connecting.component.spec.ts create mode 100644 src/app/connecting/connecting.component.ts create mode 100644 src/app/file-explorer/CREDIT create mode 100644 src/app/file-explorer/file-explorer.component.css create mode 100644 src/app/file-explorer/file-explorer.component.html create mode 100644 src/app/file-explorer/file-explorer.component.ts create mode 100644 src/app/file-explorer/file-explorer.module.ts create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.css create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts create mode 100644 src/app/file-explorer/modals/renameDialog/renameDialog.component.css create mode 100644 src/app/file-explorer/modals/renameDialog/renameDialog.component.html create mode 100644 src/app/file-explorer/modals/renameDialog/renameDialog.component.ts create mode 100644 src/app/file-explorer/model/element.ts create mode 100644 src/app/keygen/keygen.component.css create mode 100644 src/app/keygen/keygen.component.html rename src/app/{teserrors/teserrors.component.spec.ts => keygen/keygen.component.spec.ts} (55%) create mode 100644 src/app/keygen/keygen.component.ts create mode 100644 src/app/modaldialog/modaldialog.component.css create mode 100644 src/app/modaldialog/modaldialog.component.html create mode 100644 src/app/modaldialog/modaldialog.component.spec.ts create mode 100644 src/app/modaldialog/modaldialog.component.ts create mode 100644 src/app/shareconnect/shareconnect.component.css create mode 100644 src/app/shareconnect/shareconnect.component.html create mode 100644 src/app/shareconnect/shareconnect.component.spec.ts create mode 100644 src/app/shareconnect/shareconnect.component.ts create mode 100644 src/app/strudelapplist/strudelapplist.component.css create mode 100644 src/app/strudelapplist/strudelapplist.component.html create mode 100644 src/app/strudelapplist/strudelapplist.component.spec.ts create mode 100644 src/app/strudelapplist/strudelapplist.component.ts create mode 100644 src/app/submit-app.service.spec.ts create mode 100644 src/app/submit-app.service.ts create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.css create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.html create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.spec.ts create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.ts delete mode 100644 src/app/teserrors/teserrors.component.html delete mode 100644 src/app/teserrors/teserrors.component.ts create mode 100644 src/app/transfer/transfer.component.css create mode 100644 src/app/transfer/transfer.component.html create mode 100644 src/app/transfer/transfer.component.spec.ts create mode 100644 src/app/transfer/transfer.component.ts create mode 100644 src/assets/config/authservers.json create mode 100644 src/assets/config/computesites.json create mode 100644 src/assets/config/computesites.json~ create mode 100644 src/assets/config/cvluwaapps.json create mode 100644 src/assets/config/m3apps.json create mode 100644 src/assets/config/m3staticapps.json diff --git a/e2e/app/authorisation.service.spec.ts b/e2e/app/authorisation.service.spec.ts new file mode 100644 index 0000000..3286e3a --- /dev/null +++ b/e2e/app/authorisation.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AuthorisationService } from './authorisation.service'; + +describe('AuthorisationService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AuthorisationService] + }); + }); + + it('should be created', inject([AuthorisationService], (service: AuthorisationService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/e2e/app/authorisation.service.ts b/e2e/app/authorisation.service.ts new file mode 100644 index 0000000..8326754 --- /dev/null +++ b/e2e/app/authorisation.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthorisationService { + + constructor() { } +} diff --git a/package-lock.json b/package-lock.json index 19ec01c..46d1f11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dev": true, "requires": { "@angular-devkit/core": "0.6.8", - "rxjs": "6.2.0" + "rxjs": "6.3.3" } }, "@angular-devkit/build-angular": { @@ -54,7 +54,7 @@ "postcss-url": "7.3.2", "raw-loader": "0.5.1", "resolve": "1.7.1", - "rxjs": "6.2.0", + "rxjs": "6.3.3", "sass-loader": "7.0.3", "silent-error": "1.1.0", "source-map-support": "0.5.6", @@ -119,7 +119,7 @@ "requires": { "ajv": "6.4.0", "chokidar": "2.0.3", - "rxjs": "6.2.0", + "rxjs": "6.3.3", "source-map": "0.5.7" }, "dependencies": { @@ -472,23 +472,32 @@ "dev": true, "requires": { "@angular-devkit/core": "0.6.8", - "rxjs": "6.2.0" + "rxjs": "6.3.3" } }, "@angular/animations": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.0.4.tgz", - "integrity": "sha512-Ro1XEwShk8XRAogfu73fKLTBLnND1s+MGhN+ymwr7ib9hqxVr7jMNE+MXPqG2/1BX9c+7NHb30B8G4woQn62vw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.1.1.tgz", + "integrity": "sha512-iTNxhPPraCZsE4rgM23lguT1kDV4mfYAr+Bsi5J0+v9ZJA+VaKvi6eRW8ZGrx4/rDz6hzTnBn1jgPppHFbsOcw==", "requires": { "tslib": "1.9.0" } }, "@angular/cdk": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.0.0.tgz", - "integrity": "sha512-GVWUwmKWJPkK4gJTi0tgaLDs5QlRvkozIs6KnrsozkPUNDIsZyQCyEUB+llHiUB9AeDGcCDbpQyGIDLdya5khQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.1.0.tgz", + "integrity": "sha512-dY740pKcIRtKr6n6NomrgqfdEj988urTZ9I/bfJjxF5fdhnSjyhEvDlB55EHsrF+bTTZbZXRmv7AwOQ9GJnD9w==", "requires": { + "parse5": "5.1.0", "tslib": "1.9.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "optional": true + } } }, "@angular/cli": { @@ -504,7 +513,7 @@ "@schematics/update": "0.6.8", "opn": "5.3.0", "resolve": "1.7.1", - "rxjs": "6.2.0", + "rxjs": "6.3.3", "semver": "5.5.0", "silent-error": "1.1.0", "symbol-observable": "1.2.0", @@ -512,99 +521,258 @@ } }, "@angular/common": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.0.4.tgz", - "integrity": "sha512-z2UE6a43u49omsqjzwdHhwD+wpCPauuD13FMkVQMLwzugKhNri0LjMk5I0I9xLz9OyXICm2B+wPAQN4d07savg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.1.1.tgz", + "integrity": "sha512-SngekFx9v39sjgi9pON0Wehxpu+NdUk7OEebw4Fa8dKqTgydTkuhmnNH+9WQe264asoeCt51oufPRjIqMLNohA==", "requires": { "tslib": "1.9.0" } }, "@angular/compiler": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.0.4.tgz", - "integrity": "sha512-qwjuPacuRKUroD+xev653mljV9Jwhi2tvwGo/cVadq0isJ2rVH1hO6sa1FWFnNf1KPnyolMobZlMgg8DFW/yRw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.1.1.tgz", + "integrity": "sha512-oJvBe8XZ+DXF/W/DxWBTbBcixJTuPeZWdkcZIGWhJoQP7K5GnGnj8ffP9Lp6Dh4TKv85awtC6OfIKhbHxa650Q==", "requires": { "tslib": "1.9.0" } }, "@angular/compiler-cli": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.0.4.tgz", - "integrity": "sha512-bkN21JJrsbIdXNfOc9I9iB7AETUe4/QvApC25R3/pERSyhkK7UNNHTqg4FY5xMdSxlZd1ccWj8rvSbS+hFbTBw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.1.1.tgz", + "integrity": "sha512-4NXlkDhOEQgaP3Agigqw93CvXJvsfnXa0xiglq9e/wjL+6XbtM9WcDb5lfRQz41N9RSkO3pEHGvKMweKZGgogA==", "dev": true, "requires": { + "canonical-path": "1.0.0", "chokidar": "1.7.0", + "convert-source-map": "1.5.1", + "dependency-graph": "0.7.2", + "magic-string": "0.25.1", "minimist": "1.2.0", "reflect-metadata": "0.1.12", - "tsickle": "0.29.0" + "shelljs": "0.8.3", + "source-map": "0.6.1", + "tslib": "1.9.0", + "yargs": "9.0.1" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", + "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", + "dev": true, + "requires": { + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "4.1.0" + } } } }, "@angular/core": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.0.4.tgz", - "integrity": "sha512-WCW8wyRoIKkjGjOjVLFpUyhQw/K0//s3W0qo1/vH6m9njC63jP/VP+2Xnt8bdtgeET2NYUeyl+w/0vUXqYlnEA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.1.1.tgz", + "integrity": "sha512-Osig5SRgDRQ+Hec/liN7nq/BCJieB+4/pqRh9rFbOXezb2ptgRZqdXOXN8P17i4AwPVf308Mh55V0niJ5Eu3Rw==", + "requires": { + "tslib": "1.9.0" + } + }, + "@angular/flex-layout": { + "version": "7.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.19.tgz", + "integrity": "sha512-MXq+zZ6/s5/+GsL9fZ42mKL0LjZ/+L0sVU5FaQuSAJ57soLl5QAGWvdxVmROtqcHd3Htp35R49nKSZBJ0nfAjg==", "requires": { "tslib": "1.9.0" } }, "@angular/forms": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.0.4.tgz", - "integrity": "sha512-JcKyCCkAwF3NlWuwPxtpRhFX/8QbFUkA4W0N62L+Odm8G0/P4QhMw/99o5kRs5xrsQRVyFlGvdYOrkgDyyZkGg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.1.1.tgz", + "integrity": "sha512-yCWuPjpu23Wc3XUw7v/ACNn/e249oT0bYlM8aaMQ1F5OwrmmC4NJC12Rpl9Ihza61RIHIKzNcHVEgzc7WhcSag==", "requires": { "tslib": "1.9.0" } }, "@angular/http": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.0.4.tgz", - "integrity": "sha512-bq7cS0/5hM3Af3FolEe7HpKP19/7MJWIPGi5k9rYCGM4dXKFiKa+LA/SbXE0DMiKxhAQbZDwQ9HN04r70DRyBQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.1.1.tgz", + "integrity": "sha512-pRk+c/kz9aJ8te5xzCxlPLpFnwB0d/E9YkOo3/ydaXF9vZw13RTzk00YyzJ41PDzJf8oPDdXtueTQ+vtJ7Srtw==", "requires": { "tslib": "1.9.0" } }, "@angular/language-service": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.0.4.tgz", - "integrity": "sha512-LyFh/sFtnyjJJYO5UKM31nGSSkmGYFMwqzwbG+l0QNQXNoa9jj+OApvu0oOY5F0WP9DBrOAzj74j2M5olprtMQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.1.1.tgz", + "integrity": "sha512-X+5g20PMtNRGZIa3svMv4PLJdJehn4wqrS8nwOtzH5XkSn5vA3IxjsJVdSzAy2AN0/sKKJK5jmQorPtKO4saJg==", "dev": true }, "@angular/material": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.0.0.tgz", - "integrity": "sha512-g6P6yg2Gmn+LJKO+KxRnH5FL1D/dApwb/vloA9UTEzddhHoUB5JdRhlE78yQTwmtrP/cCJJih6ssZkWKRAqEbg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.1.0.tgz", + "integrity": "sha512-bgotNpSfGLjNZ1AcTyhs6XS7trF4I7UHwQmfa0l8y3Gf9plwErPDfQe2XqnayRyG9nTwHj9f1lQ45X5mr3/0/A==", "requires": { "tslib": "1.9.0" } }, "@angular/platform-browser": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.0.4.tgz", - "integrity": "sha512-4y6rSqOLuBs+RL0CIM+RZ5T8a5Tm1NisZcTUGvM8lVYPNkqTzNfHQXhBq8mTgRw9JopEwOj+UEqBbgrNX715yQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.1.1.tgz", + "integrity": "sha512-I6OPjecynGJSbPtzu0gvEgSmIR6X6/xEAhg4L9PycW1ryjzptTC9klWRTWIqsIBqMxhVnY44uKLeRNrDwMOwyA==", "requires": { "tslib": "1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.0.4.tgz", - "integrity": "sha512-uh7MoQi9kyKPFHrD7HPOU5gWyhF4pPJuGltyMTOtM/f5TNL9QQI2EugijKnnSFLQ8+7UQjmFAVeRqH9jylhIyQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.1.1.tgz", + "integrity": "sha512-ZIu48Vn4S6gjD7CMbGlKGaPQ8v9rYkWzlNYi4vTYzgiqKKNC3hqLsVESU3mSvr5oeQBxSIBidTdHSyafHFrA2w==", "requires": { "tslib": "1.9.0" } }, "@angular/router": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.0.4.tgz", - "integrity": "sha512-IKUtbbRtHN8I+Hp8/pLqBPyXJmQHrPFGzKU3H43TovD++mmT5AaYVCeJgkGUB//wjtLN3y0X/yrmyWFWcN35Gw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.1.1.tgz", + "integrity": "sha512-jbnqEq/1iDBkeH8Vn13hauGPTzhwllWM+MLfmdNGTiMzGRx4pmkWa57seDOeBF/GNYBL9JjkWTCrkKFAc2FJKw==", "requires": { "tslib": "1.9.0" } @@ -629,6 +797,14 @@ "@angular-devkit/core": "0.6.8", "@angular-devkit/schematics": "0.6.8", "typescript": "2.7.2" + }, + "dependencies": { + "typescript": { + "version": "2.7.2", + "resolved": "http://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", + "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "dev": true + } } }, "@schematics/update": { @@ -640,7 +816,7 @@ "@angular-devkit/core": "0.6.8", "@angular-devkit/schematics": "0.6.8", "npm-registry-client": "8.5.1", - "rxjs": "6.2.0", + "rxjs": "6.3.3", "semver": "5.5.0", "semver-intersect": "1.3.1" } @@ -2259,6 +2435,12 @@ "integrity": "sha512-Y1ecA1cL9wg0vni8t33nBw/poX8ypm+2c3fbwAESj8cm4ufK9CBFQ1+nUK8Dp5dtFo5Fc3JzkI5DKmQbuIo6hQ==", "dev": true }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -2398,7 +2580,6 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, - "optional": true, "requires": { "string-width": "1.0.2", "strip-ansi": "3.0.1", @@ -3185,6 +3366,12 @@ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, + "dependency-graph": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", + "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", + "dev": true + }, "deps-sort": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", @@ -6270,6 +6457,12 @@ "meow": "3.7.0" } }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -7716,6 +7909,15 @@ "yallist": "2.1.2" } }, + "magic-string": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz", + "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==", + "dev": true, + "requires": { + "sourcemap-codec": "1.4.4" + } + }, "mailcomposer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", @@ -9818,6 +10020,15 @@ "set-immediate-shim": "1.0.1" } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.7.1" + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -10147,9 +10358,9 @@ } }, "rxjs": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.0.tgz", - "integrity": "sha512-qBzf5uu6eOKiCZuAE0SgZ0/Qp+l54oeVxFfC2t+mJ2SFI6IB8gmMdJHs5DUMu5kqifqcCtsKS2XHjhZu6RKvAw==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "requires": { "tslib": "1.9.0" } @@ -10496,6 +10707,17 @@ "jsonify": "0.0.0" } }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "dev": true, + "requires": { + "glob": "7.1.2", + "interpret": "1.1.0", + "rechoir": "0.6.2" + } + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -10831,6 +11053,12 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz", + "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==", + "dev": true + }, "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", @@ -11520,42 +11748,6 @@ } } }, - "tsickle": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.29.0.tgz", - "integrity": "sha512-JpID0Lv8/irRtPmqJJxb5fCwfZhjZeKmav9Zna7UjqVuJoSbI49Wue/c2PPybX1SbRrjl7bbI/JsCl0dSUJygA==", - "dev": true, - "requires": { - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map": "0.6.1", - "source-map-support": "0.5.6" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", - "dev": true, - "requires": { - "buffer-from": "1.0.0", - "source-map": "0.6.1" - } - } - } - }, "tslib": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", @@ -11673,9 +11865,9 @@ "dev": true }, "typescript": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", - "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", + "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index 3fd2d28..1c6ee98 100644 --- a/package.json +++ b/package.json @@ -12,29 +12,30 @@ }, "private": true, "dependencies": { - "@angular/animations": "6.0.4", - "@angular/cdk": "^6.0.0", - "@angular/common": "6.0.4", - "@angular/compiler": "6.0.4", - "@angular/core": "6.0.4", - "@angular/forms": "6.0.4", - "@angular/http": "6.0.4", - "@angular/material": "^6.0.0", - "@angular/platform-browser": "6.0.4", - "@angular/platform-browser-dynamic": "6.0.4", - "@angular/router": "6.0.4", + "@angular/animations": "7.1.1", + "@angular/cdk": "^7.1.0", + "@angular/common": "7.1.1", + "@angular/compiler": "7.1.1", + "@angular/core": "7.1.1", + "@angular/flex-layout": "^7.0.0-beta.19", + "@angular/forms": "7.1.1", + "@angular/http": "7.1.1", + "@angular/material": "^7.1.0", + "@angular/platform-browser": "7.1.1", + "@angular/platform-browser-dynamic": "7.1.1", + "@angular/router": "7.1.1", "core-js": "^2.4.1", "hammerjs": "^2.0.8", "keypair": "^1.0.1", "node-forge": "^0.7.5", - "rxjs": "^6.2.0", + "rxjs": "^6.3.3", "rxjs-compat": "^6.2.0", "zone.js": "^0.8.26" }, "devDependencies": { "@angular/cli": "6.0.8", - "@angular/compiler-cli": "6.0.4", - "@angular/language-service": "6.0.4", + "@angular/compiler-cli": "7.1.1", + "@angular/language-service": "7.1.1", "@types/jasmine": "~2.8.3", "@types/jasminewd2": "~2.0.2", "@types/node": "~6.0.60", @@ -49,7 +50,7 @@ "protractor": "~5.1.2", "ts-node": "~4.1.0", "tslint": "~5.9.1", - "typescript": "2.7.2", + "typescript": "3.1.6", "@angular-devkit/build-angular": "~0.6.8" } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index dcd7bea..bb6fdc4 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -3,6 +3,9 @@ import { RouterModule, Routes } from '@angular/router'; import { LauncherComponent } from './launcher/launcher.component'; import { KeygenComponent } from './keygen/keygen.component'; import { ConnectingComponent } from './connecting/connecting.component'; +import { TransferComponent } from './transfer/transfer.component'; +import { ShareconnectComponent } from './shareconnect/shareconnect.component'; + // import { TokenextractorComponent } from './tokenextractor/tokenextractor.component'; @@ -13,7 +16,10 @@ const routes: Routes = [ { path: 'finishlaunch', component: LauncherComponent}, { path: 'cancellaunch', component: LauncherComponent}, { path: 'sshauthz_callback', component: KeygenComponent}, - { path: 'connecting', component: ConnectingComponent } + { path: 'connecting', component: ConnectingComponent }, + { path: 'transfer', component: TransferComponent }, + { path: 'shareconnect', component: ShareconnectComponent } + // { path: 'sshauthz_callback', component: LauncherComponent} diff --git a/src/app/app.component.css b/src/app/app.component.css index 31ef901..8b13789 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -1,10 +1 @@ -.fill-remaining-space { - /* This fills the remaining space, by using flexbox. - Every toolbar row uses a flexbox row layout. */ - flex: 1 1 auto; -} -.fill-horizontal-space { - /* This fills the remaining space, by using flexbox. - Every toolbar row uses a flexbox row layout. */ - flex: 0 1 auto; -} + diff --git a/src/app/app.component.html b/src/app/app.component.html index 11d6bd5..a54e574 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,5 +1,2 @@ - - -<router-outlet class="fill-remaining-space"></router-outlet> - -<!-- <app-teserrors></app-teserrors> --> +<!-- <router-outlet class="fill-remaining-space"></router-outlet> --> +<router-outlet></router-outlet> diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 355c770..6077c5e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,6 +1,10 @@ import { Component } from '@angular/core'; import { TesService } from './tes.service'; import { AuthorisationService} from './authorisation.service'; +import { ComputesitesService} from './computesites.service'; +import {BehaviorSubject} from 'rxjs/BehaviorSubject'; +import { MatSnackBar } from '@angular/material'; + @Component({ selector: 'app-root', @@ -12,16 +16,39 @@ export class AppComponent { private loggedin: Boolean = false; private errmsg: string=''; public authorised: boolean; + public statusMsg: BehaviorSubject<string>; + private snackBarRef: any; + // private testingAuth: boolean; - constructor(private tesService: TesService, private authService: AuthorisationService) { + constructor(private tesService: TesService, + private authService: AuthorisationService, + private computesitesService: ComputesitesService, + public snackBar: MatSnackBar,) { + }; ngOnInit() { // this.tesService.testingAuth.subscribe(testingAuth => { this.testingAuth = testingAuth; console.log('testingAuth updated'+this.testingAuth) }); // this.testingAuth = false; + this.statusMsg = new BehaviorSubject<any>(''); + this.tesService.setStatusMsg(this.statusMsg); + this.computesitesService.setStatusMsg(this.statusMsg); + this.authService.setStatusMsg(this.statusMsg); + this.statusMsg.subscribe(msg => this.displayMessage(msg)); } + + private displayMessage(msg) { + if (msg === null || msg == "") { + console.log('attempting to dismis snackbar'); + if (this.snackBarRef != undefined) { + this.snackBarRef.dismiss() + } + } else { + this.snackBarRef = this.snackBar.open(msg,'Dismiss'); + } + } // login() { // console.log("login"); // this.authService.login() diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 44d79aa..4c53c93 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -17,17 +17,22 @@ import { MatToolbarModule } from '@angular/material'; import { MatDialogModule, MatDialog } from '@angular/material'; import { MatSnackBarModule } from "@angular/material"; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ComputesitesService } from './computesites.service'; import { StrudelappsService } from './strudelapps.service'; import { AuthorisationService } from './authorisation.service'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { FileExplorerModule } from './file-explorer/file-explorer.module'; + import { TesService} from './tes.service'; +import { SubmitAppService } from './submit-app.service'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { JobComponent } from './job/job.component'; // import { SiteselectionComponent } from './siteselection/siteselection.component'; -import { TeserrorsComponent } from './teserrors/teserrors.component'; +// import { TeserrorsComponent } from './teserrors/teserrors.component'; import { AppRoutingModule } from './/app-routing.module'; import { TokenextractorComponent } from './tokenextractor/tokenextractor.component'; // import { LogindialogComponent } from './logindialog/logindialog.component'; @@ -39,6 +44,10 @@ import { MatSidenavModule, MatExpansionModule, MatIconModule } from '@angular/ma import { StrudelapplistComponent } from './strudelapplist/strudelapplist.component'; import { KeygenComponent } from './keygen/keygen.component'; import { ConnectingComponent } from './connecting/connecting.component'; +import { TransferComponent } from './transfer/transfer.component'; +import { Sv2SideNavComponent } from './sv2-side-nav/sv2-side-nav.component'; +import { ShareconnectComponent } from './shareconnect/shareconnect.component'; +// import { FileExplorerModule } from './file-explorer/file-explorer.module'; @@ -50,14 +59,17 @@ import { ConnectingComponent } from './connecting/connecting.component'; JoblistComponent, JobComponent, // SiteselectionComponent, - TeserrorsComponent, + // TeserrorsComponent, TokenextractorComponent, LogoutdialogComponent, ModaldialogComponent, // LaunchdialogComponent, StrudelapplistComponent, KeygenComponent, - ConnectingComponent + ConnectingComponent, + TransferComponent, + Sv2SideNavComponent, + ShareconnectComponent ], imports: [ BrowserModule, @@ -80,10 +92,14 @@ import { ConnectingComponent } from './connecting/connecting.component'; MatExpansionModule, MatIconModule, MatSnackBarModule, + FileExplorerModule, + FlexLayoutModule, + MatProgressBarModule, + ], entryComponents: [ ModaldialogComponent, LogoutdialogComponent, ], - providers: [ StrudelappsService, ComputesitesService, TesService, MatDialog, AuthorisationService], + providers: [ StrudelappsService, ComputesitesService, TesService, SubmitAppService, MatDialog, AuthorisationService], bootstrap: [AppComponent] }) export class AppModule { } diff --git a/src/app/authorisation.service.ts b/src/app/authorisation.service.ts index 51fa587..0047aa7 100644 --- a/src/app/authorisation.service.ts +++ b/src/app/authorisation.service.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { catchError, map, tap } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; -import {LocationStrategy} from '@angular/common'; +import {LocationStrategy, Location} from '@angular/common'; // import { keypair } from 'keypair'; import * as keypair from 'keypair'; import * as forge from "node-forge"; @@ -13,6 +13,8 @@ import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {Subject} from 'rxjs/Subject'; import {TesService} from './tes.service'; import { throwError } from 'rxjs'; +import { environment } from '../environments/environment'; + export class SshauthzServer {} @@ -24,26 +26,38 @@ export class AuthorisationService { // public token: BehaviorSubject<AuthToken>; public token: AuthToken public SshAuthzServers: BehaviorSubject<SshAuthzServer[]>; - public readyToNavigate: Subject<Boolean>; + public readyToNavigate: Subject<[Boolean,string]>; public progress: Subject<string>; // private keyCert: Subject<KeyCert>; + private backendURI: string; + public statusMsg: BehaviorSubject<string>; + public agentContents: BehaviorSubject<any>; + public keys: KeyCert[]; constructor(private http: HttpClient, private locationStrategy: LocationStrategy, private route: ActivatedRoute, private router: Router, - private tesService: TesService) { + private location: Location) { console.log('created AuthorisationService'); // this.token = new BehaviorSubject<AuthToken>(new AuthToken('','')); - this.readyToNavigate = new Subject<Boolean>(); - this.readyToNavigate.next(false); + this.readyToNavigate = new Subject<[Boolean,string]>(); + this.readyToNavigate.next([false,'']); this.progress = new Subject<string>(); this.progress.next(""); // this.token.subscribe(token => this.getCert(token)); this.route.fragment.subscribe(frag => this.storeToken(frag)); this.SshAuthzServers = new BehaviorSubject<SshAuthzServer[]>([]); + this.backendURI = environment.tesurl; + this.agentContents = new BehaviorSubject(null); + this.keys = []; this.getSshAuthzServers(); + this.keys = []; + } + + public setStatusMsg(statusMsg: BehaviorSubject<any>) { + this.statusMsg = statusMsg; } getSshAuthzServers() { @@ -56,6 +70,7 @@ export class AuthorisationService { updateSshAuthzServers(resp) { this.SshAuthzServers.next(<SshAuthzServer[]>resp); + console.log('sshauthzservers set to',this.SshAuthzServers.value); } storeToken(frag: string) { @@ -109,60 +124,163 @@ export class AuthorisationService { let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.sshauthzservice.sign}; - console.log('posting to getcert',this.tesService.Base); - this.http.post<any>(this.tesService.Base+'/getcert',data, options) - .pipe(catchError(this.handleError('getCert',[]))) + console.log('posting to getcert',this.backendURI); + this.http.post<any>(this.backendURI+'/getcert',data, options) + // .pipe(catchError(this.handleError([]))) .subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.sshauthzservice), error => this.httperrorLogout(error,token.sshauthzservice)); console.log('getcert complete'); } +public getKeys(id?: Identity) { + try{ + return JSON.parse(sessionStorage.getItem('keys')); + } catch { + return []; + } +} makeKeyCert(key: string, resp, sshauthzservice: SshAuthzServer) { let keyCert = new KeyCert() keyCert.key = key; keyCert.cert = resp['cert']; + console.log('in make KeyCert',resp); console.log('updating keycert',keyCert); - this.tesService.sshAdd(keyCert); + var keys: KeyCert[] = []; + try{ + keys = JSON.parse(sessionStorage.getItem('keys')); + } catch { + keys = []; + } + if (keys === null) { + keys = []; + } + keys.push(keyCert); + sessionStorage.setItem('keys',JSON.stringify(keys)) + this.sshAdd(keyCert); + let path=localStorage.getItem('path'); + console.log('authorisation complete, ready to navigate back to the correct view',path); + // this.tesService.keyCert.next(keyCert); // As soon as the certificate has been generated, we log back out of the signing server if (!(sshauthzservice.logout === null)) { window.open(sshauthzservice.logout); } - this.readyToNavigate.next(true); + this.readyToNavigate.next([true,path]); + } + + public querySshAgentError(error: any) { + this.agentContents.next([]); + if (error.status == 0) { + this.statusMsg.next("A network error occured. Are you connected to the internet?") + } + } + + public updateAgentContents() { + this.statusMsg.next("Updating the list of available accounts"); + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + var anyvar: any; + this.http.get<any>(this.backendURI+'/sshagent',options) + // .pipe(catchError(this.handleError(anyvar))) + .subscribe(resp => { console.log('sshagent returned',resp); this.agentContents.next(resp); this.statusMsg.next("") }, + error => this.querySshAgentError(error)); + // .subscribe(resp => this.computeSitesService.updateIdentities(resp), + // error => this.httperror(error)) + } + + private killAgent() { + this.statusMsg.next("Logging out") + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + var anyvar: any; + this.http.delete<any>(this.backendURI+'/sshagent',options) + .pipe(catchError(this.handleError(anyvar))) + .subscribe(resp => this.updateAgentContents(), + error => this.httperror(error)); + } + + public logout(): Boolean { + this.killAgent(); + return true; + } + + + + public sshAdd(keyCert: KeyCert) { + console.log('in authorisation service sshAdd'); + if (keyCert.key == undefined) { + return; + } + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + var anyvar: any; + + this.statusMsg.next("Authorising ...") + let data = {'key': keyCert.key, 'cert': keyCert.cert}; + this.http.post<any>(this.backendURI+'/sshagent',data,options) + .pipe(catchError(this.handleError(anyvar))) + .subscribe(resp => this.updateAgentContents(), + error => this.httperror(error)) } public login(authservice: SshAuthzServer) { let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback"; let nonce="asdfzxcv"; + + console.log(this.location.path()); + + localStorage.setItem('authservice', JSON.stringify([authservice,nonce])); + localStorage.setItem('path', this.location.path()); + console.log('login activated, pathname stored as',window.location.pathname); if (authservice.scope == null) { window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id); } else { window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id+"&scope="+authservice.scope); - } } + // private handleError(error: HttpErrorResponse) { + // if (error.error instanceof ErrorEvent) { + // console.log('instance of ErrorEvent'); + // console.log(error); + // // A client-side or network error occurred. Handle it accordingly. + // return throwError("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running."); + // } else { + // console.log('NOT an instance of ErrorEvent'); + // console.log(error); + // // Not sure if this code works correctly. It should update identities in case the error is + // // that the user isn't allowed to run the job + // + // var re = /identity/gi; + // let searchresult = error.error.message.search(re); + // if (searchresult != -1) { + // // this.getIdentities(); + // return throwError('login expired, refreshing'); + // } + // return throwError(error.error.message) + // // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message); + // + // } + private httperror(error: any) { - this.tesService.statusMsg.next(error); + this.statusMsg.next(error); console.log(error); } private httperrorLogout(error: any,sshauthzservice: SshAuthzServer) { - this.tesService.statusMsg.next(error); + this.statusMsg.next(error); console.log(error); if (!(sshauthzservice.logout === null)) { window.open(sshauthzservice.logout); } } - private handleError<T> (operation = 'operation', result?: T) { + private handleError<T> (result?: T) { return (error: any): Observable<T> => { - console.log('in handle error',operation); - console.log(error.status); - console.error(error); + console.log(error) if (error.status == 500) { - return throwError("The authorisation server encountered and error. Please try again in a few minutes") + return throwError("The backend server encountered and error. Please try again in a few minutes") } return throwError(error.message); // return of(result as T); diff --git a/src/app/batchinterface.ts b/src/app/batchinterface.ts index 68f2435..c7d78d4 100644 --- a/src/app/batchinterface.ts +++ b/src/app/batchinterface.ts @@ -2,5 +2,4 @@ export class BatchInterface { cancelcmd: string; submitcmd: string; statcmd: string; - internalfirewall: boolean; } diff --git a/src/app/computesite.ts b/src/app/computesite.ts index 695f355..f5d81fc 100644 --- a/src/app/computesite.ts +++ b/src/app/computesite.ts @@ -6,11 +6,15 @@ export class Computesite { // submit stat and cancel name: string; // Human readable name host: string; // Login host + dtn: string; + dtnport: string; + lscmd: string; cafingerprint: string; // Certificates contain a CA fingerprint. We use this // to figure out which compute site a certificate is valid // for appCatalog: BehaviorSubject<Strudelapp[]>; appCatalogUri: string; + internalfirewall: boolean; // Does a firewall exist within the site necessitating an extra level of ssh tunnel } diff --git a/src/app/computesites.service.ts b/src/app/computesites.service.ts index 4577d55..a8d0dcd 100644 --- a/src/app/computesites.service.ts +++ b/src/app/computesites.service.ts @@ -5,6 +5,9 @@ import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@a import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { catchError, map, tap } from 'rxjs/operators'; +import { Identity } from './identity'; +import { TesService } from './tes.service'; +import { AuthorisationService} from './authorisation.service'; @@ -13,24 +16,54 @@ import { catchError, map, tap } from 'rxjs/operators'; }) export class ComputesitesService { public computesites: BehaviorSubject<Computesite[]>; + public identities: BehaviorSubject<Identity[]>; + public appidentities: BehaviorSubject<Identity[]>; + public ftidentities: BehaviorSubject<Identity[]>; + private statusMsg: BehaviorSubject<any>; - constructor(private http: HttpClient,) { + constructor(private http: HttpClient, private authorisationService: AuthorisationService) { this.computesites = new BehaviorSubject<Computesite[]>([]); + this.identities = new BehaviorSubject<Identity[]>([]); + this.appidentities = new BehaviorSubject<Identity[]>([]); + this.ftidentities = new BehaviorSubject<Identity[]>([]); + this.computesites.subscribe(computesites => this.getStrudelApps(computesites)) this.getComputeSites(); + this.authorisationService.agentContents.subscribe(ac => this.updateIdentities(ac)); + } + + public setStatusMsg(statusMsg: BehaviorSubject<any>) { + this.statusMsg = statusMsg; } getStrudelApps(computesites: Computesite[]) { for (let s of computesites) { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: false}; - this.http.get<Strudelapp[]>(s.appCatalogUri,options) - .pipe(catchError(this.handleError('getStrudelApps'))) - .subscribe(resp => this.updateStrudelApps(s.appCatalog,resp)); + if (s.appCatalogUri !== null) { + this.http.get<Strudelapp[]>(s.appCatalogUri,options) + .pipe(catchError(this.handleError('getStrudelApps'))) + .subscribe(resp => this.updateStrudelApps(s.appCatalog,resp,s.name)); + } } } + + updateStrudelApps(appCatalog: BehaviorSubject<Strudelapp[]>,apps, sitename) { + var sapps: Strudelapp[]; + var localapps: Strudelapp[]; + sapps = <Strudelapp[]>apps; + + localapps = JSON.parse(localStorage.getItem(sitename+'-apps')) + if (localapps !== null) { + for (let a of localapps) { + sapps.push(a); + } + } + appCatalog.next(sapps); + } + private handleError<T> (operation = 'operation', result?: T) { return (error: any): Observable<T> => { console.error(error); @@ -38,9 +71,6 @@ export class ComputesitesService { }; } - updateStrudelApps(appCatalog: BehaviorSubject<Strudelapp[]>,apps) { - appCatalog.next(<Strudelapp[]>apps); - } getComputeSites() { let headers = new HttpHeaders(); @@ -51,12 +81,21 @@ export class ComputesitesService { } updateComputeSites(resp) { + var localcomputesites: Computesite[] = []; var computesites: Computesite[] = [] + localcomputesites = JSON.parse(localStorage.getItem('localcomputesites')) + if (localcomputesites !== null ) { + for (let cs of localcomputesites) { + cs.appCatalog = new BehaviorSubject<Strudelapp[]>([]); + computesites.push(cs); + } + } for (let cs of resp) { let computesite = <Computesite>cs; computesite.appCatalog = new BehaviorSubject<Strudelapp[]>([]) computesites.push(computesite); } + console.log('updating compute sites'); this.computesites.next(computesites); } @@ -64,4 +103,47 @@ export class ComputesitesService { return this.computesites.value; } + private siteMatch(cert: any, cs: Computesite): string { + var fp: string = (<Computesite>cs).cafingerprint; + if ('Signing CA' in cert) { + for (let ca of cert['Signing CA']) { + if (ca == fp) { + return cert['Principals'][0] + } + } + } + return null; + } + + private updateIdentities(resp) { + let certs = resp; + var cs: Computesite; + var identities: Identity[] = []; + var appidentities: Identity[] = []; + var ftidentities: Identity[] = []; + console.log('compute sites value', this.computesites.value); + console.log('certs',certs); + for (cs of this.computesites.value) { + for (let i in certs) { + let principal = this.siteMatch(certs[i],cs); + if (principal != null) { + let id = new Identity(principal,cs); + identities.push(id); + if (cs.appCatalogUri != null) { + appidentities.push(id); + } + if (cs.dtn != null ) { + ftidentities.push(id); + } + } + } + } + this.identities.next(identities); + this.ftidentities.next(ftidentities); + console.log('setting ftidentities'); + console.log(this.ftidentities.value); + this.appidentities.next(appidentities); + + } + } diff --git a/src/app/teserrors/teserrors.component.css b/src/app/connecting/connecting.component.css similarity index 100% rename from src/app/teserrors/teserrors.component.css rename to src/app/connecting/connecting.component.css diff --git a/src/app/connecting/connecting.component.html b/src/app/connecting/connecting.component.html new file mode 100644 index 0000000..de96f16 --- /dev/null +++ b/src/app/connecting/connecting.component.html @@ -0,0 +1,11 @@ +<mat-toolbar color="primary"> + <mat-toolbar-row> + <button mat-icon-button><mat-icon>menu</mat-icon></button> + + <span>Strudel v2.0</span> + <span class="fill-horizontal-space"></span> + </mat-toolbar-row> + </mat-toolbar> + <mat-card> + Creating a secure connection ... please wait + </mat-card> diff --git a/src/app/connecting/connecting.component.spec.ts b/src/app/connecting/connecting.component.spec.ts new file mode 100644 index 0000000..9992bb0 --- /dev/null +++ b/src/app/connecting/connecting.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConnectingComponent } from './connecting.component'; + +describe('ConnectingComponent', () => { + let component: ConnectingComponent; + let fixture: ComponentFixture<ConnectingComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ConnectingComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConnectingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/connecting/connecting.component.ts b/src/app/connecting/connecting.component.ts new file mode 100644 index 0000000..90ee12b --- /dev/null +++ b/src/app/connecting/connecting.component.ts @@ -0,0 +1,73 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { catchError } from 'rxjs/operators'; +import { throwError } from 'rxjs'; + + + +@Component({ + selector: 'app-connecting', + templateUrl: './connecting.component.html', + styleUrls: ['./connecting.component.css'] +}) +export class ConnectingComponent implements OnInit { + + constructor( private router: Router, private activatedRoute: ActivatedRoute, private http: HttpClient ) { } + + ngOnInit() { + this.activatedRoute + .queryParams + .subscribe(params => { + this.doConnect(params); + }); + } + + buildParams(params: { [key: string]: any;}): string { + let newparams = new URLSearchParams; + Object.entries(params).forEach( ([key,value]) => newparams.set(key,value)); + return newparams.toString(); + } + + doConnect(params: { [key: string]: any;}) { + console.log('doing connection'); + console.log(params); + let url = params['connecturl']; + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + let paramstr = this.buildParams(params); + // let paramstr = this.buildParams(null,identity,this.batchinterface[identity.repr()]); + this.http.get<HttpResponse<any>>(url+'?'+paramstr,options) + .pipe(catchError(this.handleError)) + .subscribe(resp => this.redirect(resp), + error => this.httperror(error)); + + + } + redirect(resp: HttpResponse<any>) { + let loc = resp['location']; + console.log('updating window location to',loc); + window.location.assign(loc); + }; + + + private httperror(errorstr: string) { + console.log(errorstr); + } + + private handleError(error: HttpErrorResponse) { + if (error.error instanceof ErrorEvent) { + console.log('instance of ErrorEvent'); + console.log(error); + // A client-side or network error occurred. Handle it accordingly. + return throwError("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running."); + } else { + console.log('NOT an instance of ErrorEvent'); + console.log(error); + return throwError(error.error.message) + // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message); + + } + } + +} diff --git a/src/app/file-explorer/CREDIT b/src/app/file-explorer/CREDIT new file mode 100644 index 0000000..e6c5700 --- /dev/null +++ b/src/app/file-explorer/CREDIT @@ -0,0 +1 @@ +With credit to https://github.com/LukasMarx/angular-file-manager diff --git a/src/app/file-explorer/file-explorer.component.css b/src/app/file-explorer/file-explorer.component.css new file mode 100644 index 0000000..e2645d5 --- /dev/null +++ b/src/app/file-explorer/file-explorer.component.css @@ -0,0 +1,39 @@ +:host { + height: 100%; + display: flex; + flex-direction: column; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.file-or-folder { + padding: 8px; + overflow: hidden; +} + +.file-or-folder-icon { + width: 50px; + height: 50px; + font-size: 50px; +} + +.pointer { + cursor: pointer; +} + +.spacer { + flex: 1 1 auto; +} +.file-table { + /* flex: 1 1 auto; */ + height: 400px; + /* width: 100%; */ +} + +.basic-container { + height: 400px; +} diff --git a/src/app/file-explorer/file-explorer.component.html b/src/app/file-explorer/file-explorer.component.html new file mode 100644 index 0000000..9407f32 --- /dev/null +++ b/src/app/file-explorer/file-explorer.component.html @@ -0,0 +1,139 @@ +<!-- +<mat-toolbar> + <mat-icon *ngIf="canNavigateUp" class="pointer" (click)="navigateUp()">arrow_upwards</mat-icon> + <span style="margin-left: 8px">{{path || 'Files'}}</span> + <span class="spacer"></span> +</mat-toolbar> --> + + +<!-- <div class="container" fxFlex fxLayout="row" fxLayoutAlign="space-between stretch"> --> + <!-- <div class="content" fxFlex fxLayout="row"> --> + <!-- <mat-grid-list cols="4" rowHeight="100px" fxFlex> + <mat-grid-tile *ngFor="let element of fileElements" class=file-or-folder> + <span [matMenuTriggerFor]="rootMenu" [matMenuTriggerData]="{element: element}" #menuTrigger="matMenuTrigger"></span> + <div fxLayout="column" fxLayoutAlign="space-between center" (click)="navigate(element)" (contextmenu)="openMenu($event, menuTrigger)"> + <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="element.isFolder">folder</mat-icon> + <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="!element.isFolder">insert_drive_file</mat-icon> + <span>{{element.name}}</span> + </div> + </mat-grid-tile> + </mat-grid-list> --> + + + <!-- <cdk-virtual-scroll-viewport itemSize="50px" style="height: 400px"> --> + <!-- <cdk-virtual-scroll-viewport itemSize="50px"> + + <div *cdkVirtualFor="let element of fileElements"> + <div fxFlex="row"> + <div fxFlex="10%"> + <mat-icon *ngIf="element.isFolder">folder</mat-icon> + <mat-icon *ngIf="!element.isFolder">insert_drive_file</mat-icon> + </div> + <div fxFlex> + {{ element.name }} + </div> + <div fxFlex="10%"> + {{ element.size }} + </div> + </div> + </div> + </cdk-virtual-scroll-viewport> --> + + + <table mat-table [dataSource]="fileElements" style="width: 100%"> + <ng-container matColumnDef="icon"> + <th mat-header-cell *matHeaderCellDef></th> + <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)"> + <mat-icon *ngIf="element.isFolder">folder</mat-icon> + <mat-icon *ngIf="!element.isFolder">insert_drive_file</mat-icon> + <div style="position: fixed" + [style.left]="contextMenuPosition.x" + [style.top]="contextMenuPosition.y" + [matMenuTriggerFor]="contextMenu" + [matMenuTriggerData]="{element: element}"> + </div> + </td> + </ng-container> + <ng-container matColumnDef="name"> + <th mat-header-cell *matHeaderCellDef>Name</th> + <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)"> + {{ element.name }} + <div style="position: fixed" + [style.left]="contextMenuPosition.x" + [style.top]="contextMenuPosition.y" + [matMenuTriggerFor]="contextMenu" + [matMenuTriggerData]="{element: element}"> + </div> + </td> + </ng-container> + <ng-container matColumnDef="size"> + <th mat-header-cell *matHeaderCellDef>Size</th> + <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)"> + {{ element.size }} + <div style="position: fixed" + [style.left]="contextMenuPosition.x" + [style.top]="contextMenuPosition.y" + [matMenuTriggerFor]="contextMenu" + [matMenuTriggerData]="{element: element}"> + </div> + </td> + </ng-container> + <ng-container matColumnDef="online"> + <th mat-header-cell *matHeaderCellDef>Online</th> + <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)"> + <div *ngIf="element.online !== undefined"> + <mat-icon *ngIf="element.online">check</mat-icon> + <mat-icon *ngIf="!element.online">close</mat-icon> + </div> + <div style="position: fixed" + [style.left]="contextMenuPosition.x" + [style.top]="contextMenuPosition.y" + [matMenuTriggerFor]="contextMenu" + [matMenuTriggerData]="{element: element}"> + </div> + </td> + </ng-container> + <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr> + <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="navigate(row)"></tr> + </table> + + + + + + <!-- <mat-list> + <mat-list-item *ngFor="let element of fileElements" class=file-or-folder> + <span [matMenuTriggerFor]="rootMenu" [matMenuTriggerData]="{element: element}" #menuTrigger="matMenuTrigger"></span> + <div fxFlex="100%"> + <div fxLayout="row" fxLayoutAlign="start none" (click)="navigate(element)" (contextmenu)="openMenu($event, menuTrigger)"> + <div> + <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="element.isFolder">folder</mat-icon> + <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="!element.isFolder">insert_drive_file</mat-icon> + </div> + <span fxFlex>{{element.name}}</span> + <span>{{element.size}}</span> + </div> + </div> + + </mat-list-item> + </mat-list> --> + + <!-- </div> --> +<!-- </div> --> + +<mat-menu #contextMenu="matMenu" [overlapTrigger]="false"> + <ng-template matMenuContent let-element="element"> + <!-- <button mat-menu-item [matMenuTriggerFor]="moveToMenu" [matMenuTriggerData]="{self: element}"> + <mat-icon>open_with</mat-icon> + <span>Move To</span> + </button> --> + <button mat-menu-item (click)="emitSendFile(element)"> + <mat-icon>send</mat-icon> + <span>Send file</span> + </button> + <button mat-menu-item (click)="openNewFolderDialog()"> + <mat-icon>send</mat-icon> + <span>New directory</span> + </button> + </ng-template> +</mat-menu> diff --git a/src/app/file-explorer/file-explorer.component.ts b/src/app/file-explorer/file-explorer.component.ts new file mode 100644 index 0000000..bd3751a --- /dev/null +++ b/src/app/file-explorer/file-explorer.component.ts @@ -0,0 +1,109 @@ +import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core'; +import { FileElement } from './model/element'; +import { MatMenu, MatMenuTrigger } from '@angular/material/menu'; +import { Observable } from 'rxjs/Observable'; +import { MatDialog } from '@angular/material/dialog'; +import { NewFolderDialogComponent } from './modals/newFolderDialog/newFolderDialog.component'; +import { RenameDialogComponent } from './modals/renameDialog/renameDialog.component'; + +@Component({ + selector: 'file-explorer', + templateUrl: './file-explorer.component.html', + styleUrls: ['./file-explorer.component.css'] +}) +export class FileExplorerComponent { + constructor(public dialog: MatDialog) {} + + @ViewChild(MatMenuTrigger) + contextMenu: MatMenuTrigger; + + contextMenuPosition = { x: '0px', y: '0px' }; + + @Input() fileElements: FileElement[]; + @Input() canNavigateUp: string; + @Input() path: string; + @Input() disableSend: Boolean; + @Input() identities: any; + + @Output() folderAdded = new EventEmitter<{ name: string }>(); + @Output() elementRemoved = new EventEmitter<FileElement>(); + @Output() elementRenamed = new EventEmitter<FileElement>(); + @Output() elementMoved = new EventEmitter<{ element: FileElement; moveTo: FileElement }>(); + @Output() navigatedDown = new EventEmitter<FileElement>(); + @Output() navigatedUp = new EventEmitter(); + @Output() sendFile = new EventEmitter<FileElement>(); + @Output() idSelected = new EventEmitter<any>(); + displayedColumns: string[] = ['icon','name','size','online']; + fixedSizeData = Array(10000).fill(30); + + onContextMenu(event: MouseEvent, element: any) { + event.preventDefault(); + console.log('called onContextMenu with element',element); + this.contextMenuPosition.x = event.clientX + 'px'; + this.contextMenuPosition.y = event.clientY + 'px'; + this.contextMenu.menuData = { 'element': element }; + this.contextMenu.openMenu(); + } + + deleteElement(element: FileElement) { + this.elementRemoved.emit(element); + } + + navigate(element: FileElement) { + console.log('attempting navigate down'); + + if (element.isFolder) { + console.log('attempting navigate down',element.name); + + this.navigatedDown.emit(element); + } + } + + navigateUp() { + console.log('attempting navigate up'); + this.navigatedUp.emit(); + console.log('emitted navigateUp'); + } + + moveElement(element: FileElement, moveTo: FileElement) { + this.elementMoved.emit({ element: element, moveTo: moveTo }); + } + + rowClicked(row) { + console.log('row clicked',row); + } + + openNewFolderDialog() { + let dialogRef = this.dialog.open(NewFolderDialogComponent); + dialogRef.afterClosed().subscribe(res => { + if (res) { + this.folderAdded.emit({ name: res }); + } + }); + } + + openRenameDialog(element: FileElement) { + let dialogRef = this.dialog.open(RenameDialogComponent); + dialogRef.afterClosed().subscribe(res => { + if (res) { + element.name = res; + this.elementRenamed.emit(element); + } + }); + } + + emitSendFile(element: FileElement) { + console.log('called emitSendFile with element',element); + this.sendFile.emit(element); + } + + emitIdSelected(id: any) { + console.log('Im going to emit',id); + this.idSelected.emit(id); + } + + openMenu(event: MouseEvent, viewChild: MatMenuTrigger) { + event.preventDefault(); + viewChild.openMenu(); + } +} diff --git a/src/app/file-explorer/file-explorer.module.ts b/src/app/file-explorer/file-explorer.module.ts new file mode 100644 index 0000000..c8ee87e --- /dev/null +++ b/src/app/file-explorer/file-explorer.module.ts @@ -0,0 +1,48 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { MatIconModule } from '@angular/material/icon'; +import { MatGridListModule } from '@angular/material/grid-list'; +import { MatListModule } from '@angular/material'; +import { MatMenuModule } from '@angular/material/menu'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatInputModule } from '@angular/material/input'; +import { NewFolderDialogComponent } from './modals/newFolderDialog/newFolderDialog.component'; +import { MatButtonModule } from '@angular/material/button'; +import { FormsModule } from '@angular/forms'; +import { RenameDialogComponent } from './modals/renameDialog/renameDialog.component'; +import { FileExplorerComponent } from './file-explorer.component'; +import {MatTableModule} from '@angular/material/table'; +import {ScrollingModule} from '@angular/cdk/scrolling'; +import {MatSidenavModule} from '@angular/material/sidenav' +import { MatSelectModule } from '@angular/material'; + + +// import {cdkScrollable} from '@angular/' + +@NgModule({ + imports: [ + CommonModule, + MatToolbarModule, + FlexLayoutModule, + MatIconModule, + MatGridListModule, + MatListModule, + MatMenuModule, + BrowserAnimationsModule, + MatDialogModule, + MatInputModule, + FormsModule, + MatButtonModule, + MatTableModule, + ScrollingModule, + MatSidenavModule, + MatSelectModule, + ], + declarations: [FileExplorerComponent, NewFolderDialogComponent, RenameDialogComponent], + exports: [FileExplorerComponent], + entryComponents: [NewFolderDialogComponent, RenameDialogComponent] +}) +export class FileExplorerModule {} diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.css b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html new file mode 100644 index 0000000..275a3a6 --- /dev/null +++ b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html @@ -0,0 +1,12 @@ +<h1 mat-dialog-title>Create a new folder</h1> + +<mat-dialog-content> + <mat-form-field class="example-full-width"> + <input matInput placeholder="Folder Name" [(ngModel)]="folderName"> + </mat-form-field> +</mat-dialog-content> + +<mat-dialog-actions> + <button mat-raised-button mat-dialog-close>Cancel</button> + <button mat-raised-button [mat-dialog-close]="folderName" color="primary">OK</button> +</mat-dialog-actions> \ No newline at end of file diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts new file mode 100644 index 0000000..eb055a6 --- /dev/null +++ b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts @@ -0,0 +1,28 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { NewFolderDialogComponent } from './newFolderDialog.component'; + +describe('NewFolderDialogComponent', () => { + let component: NewFolderDialogComponent; + let fixture: ComponentFixture<NewFolderDialogComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ NewFolderDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NewFolderDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts new file mode 100644 index 0000000..5cb47ea --- /dev/null +++ b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-newFolderDialog', + templateUrl: './newFolderDialog.component.html', + styleUrls: ['./newFolderDialog.component.css'] +}) +export class NewFolderDialogComponent implements OnInit { + constructor(public dialogRef: MatDialogRef<NewFolderDialogComponent>) {} + + folderName: string; + + ngOnInit() {} +} diff --git a/src/app/file-explorer/modals/renameDialog/renameDialog.component.css b/src/app/file-explorer/modals/renameDialog/renameDialog.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/file-explorer/modals/renameDialog/renameDialog.component.html b/src/app/file-explorer/modals/renameDialog/renameDialog.component.html new file mode 100644 index 0000000..dcc611c --- /dev/null +++ b/src/app/file-explorer/modals/renameDialog/renameDialog.component.html @@ -0,0 +1,12 @@ +<h1 mat-dialog-title>Rename Element</h1> + +<mat-dialog-content> + <mat-form-field class="example-full-width"> + <input matInput placeholder="Folder Name" [(ngModel)]="folderName"> + </mat-form-field> +</mat-dialog-content> + +<mat-dialog-actions> + <button mat-raised-button mat-dialog-close>Cancel</button> + <button mat-raised-button [mat-dialog-close]="folderName" color="primary">OK</button> +</mat-dialog-actions> \ No newline at end of file diff --git a/src/app/file-explorer/modals/renameDialog/renameDialog.component.ts b/src/app/file-explorer/modals/renameDialog/renameDialog.component.ts new file mode 100644 index 0000000..ccac0c2 --- /dev/null +++ b/src/app/file-explorer/modals/renameDialog/renameDialog.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-renameDialog', + templateUrl: './renameDialog.component.html', + styleUrls: ['./renameDialog.component.css'] +}) +export class RenameDialogComponent implements OnInit { + constructor(public dialogRef: MatDialogRef<RenameDialogComponent>) {} + + folderName: string; + + ngOnInit() {} +} diff --git a/src/app/file-explorer/model/element.ts b/src/app/file-explorer/model/element.ts new file mode 100644 index 0000000..9b91920 --- /dev/null +++ b/src/app/file-explorer/model/element.ts @@ -0,0 +1,6 @@ +export class FileElement { + isFolder: boolean; + name: string; + size: number; + online: boolean; +} diff --git a/src/app/identity.ts b/src/app/identity.ts index 63a079b..4247872 100644 --- a/src/app/identity.ts +++ b/src/app/identity.ts @@ -5,9 +5,11 @@ export class Identity { username: string; site: Computesite; authservice: SshAuthzServer; + keyCerts: KeyCert[]; constructor( username: string, site: Computesite) { this.username = username; this.site = site; + this.keyCerts = []; } copy_skip_catalog(): Identity { @@ -18,6 +20,7 @@ export class Identity { id.site.host = this.site.host; id.site.name = this.site.name; id.site.cafingerprint = this.site.cafingerprint; + id.site.dtn = this.site.dtn; return id; } displayName(): string { @@ -52,4 +55,5 @@ export class SshAuthzServer { icon: string; scope: string; logout: string; + userdefined: boolean; } diff --git a/src/app/job.ts b/src/app/job.ts index c4673f9..ec75181 100644 --- a/src/app/job.ts +++ b/src/app/job.ts @@ -1,6 +1,7 @@ import {Computesite} from './computesite'; import {Identity} from './identity'; -import {Strudelapp} from './strudelapp'; +import {Strudelapp, StrudelappInstance } from './strudelapp'; + export class Job { public name: string; public jobid: string; @@ -10,4 +11,6 @@ export class Job { public batch_host: string; public identity: Identity; public app: Strudelapp; + public appinst: string; + public connectionState: number; } diff --git a/src/app/job/job.component.html b/src/app/job/job.component.html index 2fbde39..0bdb89c 100644 --- a/src/app/job/job.component.html +++ b/src/app/job/job.component.html @@ -1,5 +1,6 @@ <mat-card> <mat-card-title>{{ jobdata.name }}</mat-card-title> + <div *ngIf="(jobdata.connectionState === undefined || jobdata.connectionState == 0)"> <table> <tr> <td width="1000%"> @@ -24,4 +25,18 @@ </td> </tr> </table> +</div> +<div *ngIf="jobdata.connectionState == 1"> + Getting app parameters + <mat-progress-bar mode="determinate" value=30></mat-progress-bar> +</div> +<div *ngIf="jobdata.connectionState == 2"> + Creating secure tunnels + <mat-progress-bar mode="determinate" value=60></mat-progress-bar> + +</div> +<div *ngIf="jobdata.connectionState == 3"> + Determining correct URL + <mat-progress-bar mode="determinate" value=90></mat-progress-bar> +</div> </mat-card> diff --git a/src/app/job/job.component.ts b/src/app/job/job.component.ts index 0998980..c7d191e 100644 --- a/src/app/job/job.component.ts +++ b/src/app/job/job.component.ts @@ -26,8 +26,12 @@ export class JobComponent implements OnInit { this.available = false; } this.tesService.busy.subscribe(busy => this.busy = busy); - if (this.jobdata.app.startscript === null) { - this.nocancel = true; + if (this.jobdata.app != null) { + if (this.jobdata.app.startscript === null) { + this.nocancel = true; + } else { + this.nocancel = false; + } } else { this.nocancel = false; } diff --git a/src/app/joblist/joblist.component.html b/src/app/joblist/joblist.component.html index ac77ade..be296f3 100644 --- a/src/app/joblist/joblist.component.html +++ b/src/app/joblist/joblist.component.html @@ -4,13 +4,13 @@ </mat-card> </div> <div *ngFor="let id of identities"> --> - - <mat-card class="fill-remaining-space"> +<mat-card style="width: 100%"> <div *ngIf="identity == undefined" > Select your username an compute site on the left to see running jobs or start new jobs. </div> <div *ngIf="!(identity == undefined)"> - <div *ngFor="let job of jobs[identity.repr()]"> + <button mat-button (click)="refreshJobs()" style="width: 100%; text-align: left">Refresh</button> + <div *ngFor="let job of tesService.joblist | async"> <app-job [jobdata]=job></app-job> </div> </div> diff --git a/src/app/joblist/joblist.component.ts b/src/app/joblist/joblist.component.ts index 5f2d2f1..883fec0 100644 --- a/src/app/joblist/joblist.component.ts +++ b/src/app/joblist/joblist.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit, Input } from '@angular/core'; import {TesService} from '../tes.service'; import { Job } from '../job'; import { Observable } from 'rxjs/Observable'; +import { Subscription, interval } from 'rxjs'; import { Identity } from '../identity'; @@ -14,41 +15,41 @@ import { Identity } from '../identity'; export class JoblistComponent implements OnInit { @Input() identity: Identity; - public jobs: {[id: string]: Job[] } = {}; - public identities: Identity[]; + // public jobs: {[id: string]: Job[] } = {}; private displayedColumns = ['id']; - private jobsSubscription: any; - private idSubscription: any; + private timer + // private jobsSubscription: Subscription; + // private idSubscription: any; + private timerSub: Subscription; constructor(private tesService: TesService,) { } ngOnInit() { - this.jobsSubscription = this.tesService.joblist.subscribe(jobs => this.updateJobs(jobs)); - // this.idSubscription = this.tesService.identities.subscribe(ids => this.updateIds(ids)); - + if (!(this.identity === undefined)) { + console.log("ini joblistComponent with id",this.identity); + this.tesService.getJobs(this.identity); + } + this.timerSub = interval(5000).subscribe(() => this.refreshJobs()) + // this.jobsSubscription = this.tesService.joblist.subscribe(jobs => this.updateJobs(jobs)); } public ngOnDestroy(): void { - if (this.jobsSubscription) { - this.jobsSubscription.unsubscribe(); + if (this.timerSub) { + this.timerSub.unsubscribe(); } } - // updateIds(identities: Identity[]) { - // this.identities = identities; + // + // + // updateJobs(jobs) { + // console.log('update jobs',jobs); + // this.jobs = jobs; // } - updateJobs(jobs) { - console.log('update jobs',jobs); - this.jobs = jobs; - + public refreshJobs(): void { + console.log('hit refresh with identitiy', this.identity); + this.tesService.getJobs(this.identity); } - - // setId(identity) { - // this.identity = identity; - // } - - } diff --git a/src/app/keygen/keygen.component.css b/src/app/keygen/keygen.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/keygen/keygen.component.html b/src/app/keygen/keygen.component.html new file mode 100644 index 0000000..c5a8e0b --- /dev/null +++ b/src/app/keygen/keygen.component.html @@ -0,0 +1,11 @@ +<mat-toolbar color="primary"> + <mat-toolbar-row> + <button mat-icon-button><mat-icon>menu</mat-icon></button> + + <span>Strudel v2.0</span> + <span class="fill-horizontal-space"></span> + </mat-toolbar-row> + </mat-toolbar> + <mat-card> + Generating cryptographic tokens ... this should only take a few seconds + </mat-card> diff --git a/src/app/teserrors/teserrors.component.spec.ts b/src/app/keygen/keygen.component.spec.ts similarity index 55% rename from src/app/teserrors/teserrors.component.spec.ts rename to src/app/keygen/keygen.component.spec.ts index 4a31e33..65b68d5 100644 --- a/src/app/teserrors/teserrors.component.spec.ts +++ b/src/app/keygen/keygen.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TeserrorsComponent } from './teserrors.component'; +import { KeygenComponent } from './keygen.component'; -describe('TeserrorsComponent', () => { - let component: TeserrorsComponent; - let fixture: ComponentFixture<TeserrorsComponent>; +describe('KeygenComponent', () => { + let component: KeygenComponent; + let fixture: ComponentFixture<KeygenComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ TeserrorsComponent ] + declarations: [ KeygenComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(TeserrorsComponent); + fixture = TestBed.createComponent(KeygenComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/keygen/keygen.component.ts b/src/app/keygen/keygen.component.ts new file mode 100644 index 0000000..d944f96 --- /dev/null +++ b/src/app/keygen/keygen.component.ts @@ -0,0 +1,46 @@ +import { Component, OnInit } from '@angular/core'; +import { AuthorisationService } from '../authorisation.service'; +import { Router, NavigationStart } from '@angular/router'; +import { timer } from 'rxjs/observable/timer'; + + + + +@Component({ + selector: 'app-keygen', + templateUrl: './keygen.component.html', + styleUrls: ['./keygen.component.css'] +}) +export class KeygenComponent implements OnInit { + + constructor(private router: Router, private authService: AuthorisationService) { } + + ngOnInit() { + // The sequence is tricky here: + // 1) the auth service loads, examines the fragment and stores the OAuth2 Token required to generate certificates + // 2) The KeyGen component becomes visible providing a Spinner + // 3) The timer(0) executes once the component is visiable causing the authService to move onto the next phase + // 4) The authservice generates ssh keys in javascript (slow) and gets them signed (also slow) + // 5) The authservice indicates its done by setting readtToNavigate => true + // 6) the router navigates to the main page (removing the spinner etc) + // ready to navigate will be set once the keys are generated + this.authService.readyToNavigate.subscribe(readyToNavigate => this.navigate(readyToNavigate)); + // use of a timer(0) means this will be executed AFTER the component is visiable (hopefully) + timer(0).subscribe( () => this.genKeys() ); + } + + genKeys() { + this.authService.getCert(this.authService.token) + } + + activateToken(token) { + console.log('activate token called'); + } + + navigate(readyToNavigate: [Boolean,string]) { + if (readyToNavigate[0]) { + this.router.navigate([readyToNavigate[1]]); + } + } + +} diff --git a/src/app/launcher/launcher.component.css b/src/app/launcher/launcher.component.css index 67e3986..683ae23 100644 --- a/src/app/launcher/launcher.component.css +++ b/src/app/launcher/launcher.component.css @@ -1,10 +1,3 @@ -.wrapper { - /* display: flex; - flex-direction: column; */ - flex: 1 1 auto; -} -.vspace { - /* display: flex; - flex-direction: column; */ -height: 500px; +:host { + height: 100%; } diff --git a/src/app/launcher/launcher.component.html b/src/app/launcher/launcher.component.html index 0315cba..642b748 100644 --- a/src/app/launcher/launcher.component.html +++ b/src/app/launcher/launcher.component.html @@ -1,3 +1,5 @@ +<div fxFlex style="flex: 1 1 0%; box-sizing: border-box"> +<div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" > <mat-toolbar color="primary"> <mat-toolbar-row> <button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button> @@ -6,23 +8,15 @@ <span class="fill-horizontal-space"></span> </mat-toolbar-row> </mat-toolbar> - -<mat-sidenav-container autosize class="fill-remaining-space"> +<mat-sidenav-container autosize autosize style="height: 100%; width: 100%"> <mat-sidenav #idSideNav mode="side" opened> - <mat-accordion> - <mat-expansion-panel (click)=selectId(undefined)> - <mat-expansion-panel-header> - <mat-panel-title> - Login - </mat-panel-title> - </mat-expansion-panel-header> - <div *ngFor="let sshauthzserver of sshauthzservers"> - <button mat-button (click)=login(sshauthzserver)>Login to {{ sshauthzserver.name }} </button> - </div> - <button mat-button (click)=logout()>Logout</button> - </mat-expansion-panel> - <div *ngFor="let id of identities"> - <mat-expansion-panel> + <mat-accordion> + <app-sv2-side-nav></app-sv2-side-nav> + </mat-accordion> + <mat-divider></mat-divider> + <mat-accordion> + <div *ngFor="let id of computeSitesService.appidentities | async"> + <mat-expansion-panel (click)=selectId(id)> <mat-expansion-panel-header> {{ id.displayName() }} </mat-expansion-panel-header> @@ -31,17 +25,17 @@ <button mat-button (click)=openLaunchWindow(app,id)>{{ app.name }}</button> </div> </div> --> - <app-strudelapplist [applist]=id.site.appCatalog [identity]=id></app-strudelapplist> + <app-strudelapplist [applist]=id.site.appCatalog [identity]="id"></app-strudelapplist> </mat-expansion-panel> </div> </mat-accordion> - </mat-sidenav> - <mat-sidenav-content> - <app-joblist [identity]=identity></app-joblist> - <div class="vspace"></div> - </mat-sidenav-content> + <app-joblist [identity]="identitySubject | async"></app-joblist> + <div fxFlex></div> + </mat-sidenav-container> +</div> +</div> <!-- <app-joblist></app-joblist> --> diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts index de59d57..dea07c4 100644 --- a/src/app/launcher/launcher.component.ts +++ b/src/app/launcher/launcher.component.ts @@ -18,6 +18,10 @@ import { LogoutdialogComponent } from '../logoutdialog/logoutdialog.component'; // import { LaunchdialogComponent } from '../launchdialog/launchdialog.component'; import { BatchInterface} from '../batchinterface'; import { SshAuthzServer } from '../identity'; +import { ComputesitesService } from '../computesites.service'; +import { Router } from '@angular/router'; +import {BehaviorSubject} from 'rxjs/BehaviorSubject'; + @@ -33,6 +37,8 @@ export class LauncherComponent implements OnInit { public app: Strudelapp; public authorised: boolean; public identity: Identity; + public identitySubject: BehaviorSubject<Identity>; + public identities: Identity[]; public sshauthzservers: SshAuthzServer[]; private launchwindow: any; @@ -40,33 +46,37 @@ export class LauncherComponent implements OnInit { constructor( public dialog: MatDialog, private tesService: TesService, - private authService: AuthorisationService) { + private authService: AuthorisationService, + public computeSitesService: ComputesitesService, + private router: Router) { authService.SshAuthzServers.subscribe(o => {this.sshauthzservers = o}); this.launchwindowWatcher = null; + this.identitySubject = new BehaviorSubject(undefined); } ngOnInit() { this.strudelapps = []; - this.identities = []; - this.tesService.identities.subscribe(identities => this.updateIdentities(identities)); + // this.identities = []; + // this.computeSitesService.identities.subscribe(identities => this.updateIdentities(identities) + // this.tesService.identities.subscribe(identities => this.updateIdentities(identities)); // setTimeout( () => { this.tesService.identities.subscribe(identities => this.updateIdentities(identities)); }); - setTimeout( () => this.tesService.getIdentities() ) + setTimeout( () => this.authService.updateAgentContents() ) } - compareFn = (o1, o2) => { - // Funny "=>" function syntax gives me access to "this" as LauncherComponent - // otherwise "this" would be set to the mat-select object. - if (o2 === undefined) { - - if (o1 === this.identity) { - return true; - } - } - if (o1 === o2) { - return true; - } - return false; - } + // compareFn = (o1, o2) => { + // // Funny "=>" function syntax gives me access to "this" as LauncherComponent + // // otherwise "this" would be set to the mat-select object. + // if (o2 === undefined) { + // + // if (o1 === this.identity) { + // return true; + // } + // } + // if (o1 === o2) { + // return true; + // } + // return false; + // } updateIdentities(identities) { this.identities = identities; @@ -84,6 +94,10 @@ login (sshauthzserver) { this.authService.login(sshauthzserver); } +navTransfer() { + this.router.navigate(['transfer']) +} + logout() { let dialogRef = this.dialog.open(LogoutdialogComponent, { width: '250px', @@ -106,6 +120,9 @@ logout() { selectId(id: Identity) { this.identity=id; + console.log('set identity subject'); + this.identitySubject.next(id); + this.tesService.getJobs(id); } diff --git a/src/app/logoutdialog/logoutdialog.component.ts b/src/app/logoutdialog/logoutdialog.component.ts index 53a5c8a..e01f5d5 100644 --- a/src/app/logoutdialog/logoutdialog.component.ts +++ b/src/app/logoutdialog/logoutdialog.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { TesService } from '../tes.service'; +import { AuthorisationService } from '../authorisation.service'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material'; @@ -13,14 +13,14 @@ export class LogoutdialogComponent implements OnInit { constructor( public dialogRef: MatDialogRef<LogoutdialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any, - private tesService: TesService) { + private authService: AuthorisationService) { } ngOnInit() { } onLogout() { - this.tesService.logout(); + this.authService.logout(); this.dialogRef.close(); } onCancel() { diff --git a/src/app/modaldialog/modaldialog.component.css b/src/app/modaldialog/modaldialog.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/modaldialog/modaldialog.component.html b/src/app/modaldialog/modaldialog.component.html new file mode 100644 index 0000000..30c2523 --- /dev/null +++ b/src/app/modaldialog/modaldialog.component.html @@ -0,0 +1,10 @@ +<p> +<mat-card> + <mat-card-title>Application help</mat-card-title> +If you're having difficultly connecting to your Application please: +<mat-list> + <mat-list-item>Only open one application at a time</mat-list-item> + <mat-list-item>Try holding shift and clicking the reload button in your browser</mat-list-item> + <mat-list-item>Close the application tab and use the connect button a second time.</mat-list-item> +</mat-list> +</mat-card> diff --git a/src/app/modaldialog/modaldialog.component.spec.ts b/src/app/modaldialog/modaldialog.component.spec.ts new file mode 100644 index 0000000..5da07fd --- /dev/null +++ b/src/app/modaldialog/modaldialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModaldialogComponent } from './modaldialog.component'; + +describe('ModaldialogComponent', () => { + let component: ModaldialogComponent; + let fixture: ComponentFixture<ModaldialogComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ModaldialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ModaldialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modaldialog/modaldialog.component.ts b/src/app/modaldialog/modaldialog.component.ts new file mode 100644 index 0000000..760256f --- /dev/null +++ b/src/app/modaldialog/modaldialog.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material'; + +@Component({ + selector: 'app-modaldialog', + templateUrl: './modaldialog.component.html', + styleUrls: ['./modaldialog.component.css'] +}) +export class ModaldialogComponent implements OnInit { + + constructor( + public dialogRef: MatDialogRef<ModaldialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: any, +) { + } + + ngOnInit() { + } + +} diff --git a/src/app/shareconnect/shareconnect.component.css b/src/app/shareconnect/shareconnect.component.css new file mode 100644 index 0000000..683ae23 --- /dev/null +++ b/src/app/shareconnect/shareconnect.component.css @@ -0,0 +1,3 @@ +:host { + height: 100%; +} diff --git a/src/app/shareconnect/shareconnect.component.html b/src/app/shareconnect/shareconnect.component.html new file mode 100644 index 0000000..c2ab412 --- /dev/null +++ b/src/app/shareconnect/shareconnect.component.html @@ -0,0 +1,38 @@ +<div fxFlex style="flex: 1 1 0%; box-sizing: border-box"> +<div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" > + <mat-toolbar color="primary"> + <mat-toolbar-row> + <button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button> + + <span>Strudel v2.0</span> + <span class="fill-horizontal-space"></span> + </mat-toolbar-row> + </mat-toolbar> + +<mat-sidenav-container #idSideNav autosize style="height: 100%; width: 100%"> + <mat-sidenav #idSideNav mode="side" opened> + <app-sv2-side-nav></app-sv2-side-nav> + </mat-sidenav> + + <div fxLayout="column" fxLayoutAlign="none" style="height: 100%; width: 100%"> + + <!-- <div style="height: 600px"></div> --> + <form> + <mat-form-field style="width: 100%; height: 100%"> + <textarea fxFlex matInput placeholder="Paste the data sent via email" style="width: 100%; height: 100%" [(ngModel)]="data" name="data"></textarea> + </mat-form-field> + </form> + + <div style="width: 100%" fxLayout="row" fxLayoutAlign="none"> + <div fxFlex></div> + <button mat-button (click)="onSubmit()">Connect</button> + <div fxFlex></div> + </div> + </div> + + + + <!-- <mat-card style="width: 100%; box-sizing: border-box">Transfer tasks</mat-card> --> +</mat-sidenav-container> +</div> +</div> diff --git a/src/app/shareconnect/shareconnect.component.spec.ts b/src/app/shareconnect/shareconnect.component.spec.ts new file mode 100644 index 0000000..8ee2c65 --- /dev/null +++ b/src/app/shareconnect/shareconnect.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ShareconnectComponent } from './shareconnect.component'; + +describe('ShareconnectComponent', () => { + let component: ShareconnectComponent; + let fixture: ComponentFixture<ShareconnectComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ShareconnectComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ShareconnectComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shareconnect/shareconnect.component.ts b/src/app/shareconnect/shareconnect.component.ts new file mode 100644 index 0000000..feb1dbb --- /dev/null +++ b/src/app/shareconnect/shareconnect.component.ts @@ -0,0 +1,71 @@ +import { Component, OnInit } from '@angular/core'; +import { Strudelapp } from '../strudelapp'; +import { StrudelappsService } from '../strudelapps.service'; +import { TesService } from '../tes.service'; +import { AuthorisationService } from '../authorisation.service'; +import { Computesite } from '../computesite'; +import { ComputesitesService } from '../computesites.service'; +import { Router } from '@angular/router'; +import { Job } from '../job'; +import { Subscription } from 'rxjs'; +import {Identity, KeyCert} from '../identity'; + + +@Component({ + selector: 'app-shareconnect', + templateUrl: './shareconnect.component.html', + styleUrls: ['./shareconnect.component.css'] +}) +export class ShareconnectComponent implements OnInit { + + data: string = ""; + invalid: boolean = false; + subscription: Subscription; + constructor(private computesitesService: ComputesitesService, + private tesService: TesService, + private authService: AuthorisationService, + private router: Router) { } + + ngOnInit() { + setTimeout( () => this.authService.updateAgentContents() ) + } + + connect(jobdata: Job) { + this.tesService.connect(jobdata); + } + + onSubmit() { + var dataobject: Object; + var jobdata: Job; + var site: Computesite; + var id: Identity; + try { + dataobject = JSON.parse(this.data); + console.log('json parsed'); + jobdata = <Job>dataobject['job']; + site = <Computesite>dataobject['site']; + id = <Identity>dataobject['identity']; + id.site = site; + jobdata.identity = new Identity(id.username, site); + + let keycert = dataobject['keycert']; + console.log('jobdata and keycert extracted'); + if (keycert !== null) { + this.subscription = this.authService.agentContents.subscribe(() => this.connect(jobdata)) + this.authService.sshAdd(keycert); + } else { + console.log('calling connect'); + this.connect(jobdata); + } + console.log('data validated'); + this.invalid=false; + } catch { + if (this.subscription !== undefined) { + this.subscription.unsubscribe(); + } + this.invalid=true; + console.log('invalid json input') + } + } + +} diff --git a/src/app/strudelapp.ts b/src/app/strudelapp.ts index 59b85b0..e420976 100644 --- a/src/app/strudelapp.ts +++ b/src/app/strudelapp.ts @@ -11,3 +11,8 @@ export class Strudelapp { /// how we create tunnels applist: Strudelapp[] = null; } + +export class StrudelappInstance { + port: number; + password: string; +} diff --git a/src/app/strudelapplist/strudelapplist.component.css b/src/app/strudelapplist/strudelapplist.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/strudelapplist/strudelapplist.component.html b/src/app/strudelapplist/strudelapplist.component.html new file mode 100644 index 0000000..36b9098 --- /dev/null +++ b/src/app/strudelapplist/strudelapplist.component.html @@ -0,0 +1,16 @@ +<div *ngFor="let app of applist | async"> + <div *ngIf="app.startscript != null"> + <button mat-button (click)=openLaunchWindow(app)>{{ app.name }}</button> + </div> + <!-- The following allows for a nested set of apps --> + <div *ngIf="app.applist != null"> + <mat-accordion> + <mat-expansion-panel> + <mat-expansion-panel-header> + {{ app.name }} + </mat-expansion-panel-header> + <app-strudelapplist [applist]=app.applist [identity]=identity></app-strudelapplist> + </mat-expansion-panel> + </mat-accordion> + </div> +</div> diff --git a/src/app/strudelapplist/strudelapplist.component.spec.ts b/src/app/strudelapplist/strudelapplist.component.spec.ts new file mode 100644 index 0000000..8214575 --- /dev/null +++ b/src/app/strudelapplist/strudelapplist.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StrudelapplistComponent } from './strudelapplist.component'; + +describe('StrudelapplistComponent', () => { + let component: StrudelapplistComponent; + let fixture: ComponentFixture<StrudelapplistComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ StrudelapplistComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(StrudelapplistComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/strudelapplist/strudelapplist.component.ts b/src/app/strudelapplist/strudelapplist.component.ts new file mode 100644 index 0000000..33bf719 --- /dev/null +++ b/src/app/strudelapplist/strudelapplist.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import {BehaviorSubject} from 'rxjs/BehaviorSubject'; +import {Strudelapp} from '../strudelapp'; +import { SubmitAppService } from '../submit-app.service'; +import { Identity } from '../identity'; +import { repeat } from 'rxjs/operators'; +import { BatchInterface} from '../batchinterface'; +import { timer } from 'rxjs/observable/timer'; + + + + + +@Component({ + selector: 'app-strudelapplist', + templateUrl: './strudelapplist.component.html', + styleUrls: ['./strudelapplist.component.css'] +}) +export class StrudelapplistComponent implements OnInit { + @Input() applist: Strudelapp[]; + @Input() identity: Identity; + + + constructor(private submitAppService: SubmitAppService ) { + } + + ngOnInit() { + } + + openLaunchWindow(app, Strudelapp) { + console.log('strudel applist openLaunchWindow'); + console.log(this.submitAppService); + this.submitAppService.launchApp(app,this.identity); + } + + +} diff --git a/src/app/submit-app.service.spec.ts b/src/app/submit-app.service.spec.ts new file mode 100644 index 0000000..0428a32 --- /dev/null +++ b/src/app/submit-app.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { SubmitAppService } from './submit-app.service'; + +describe('SubmitAppService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [SubmitAppService] + }); + }); + + it('should be created', inject([SubmitAppService], (service: SubmitAppService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/submit-app.service.ts b/src/app/submit-app.service.ts new file mode 100644 index 0000000..0efaccb --- /dev/null +++ b/src/app/submit-app.service.ts @@ -0,0 +1,158 @@ +import { Injectable } from '@angular/core'; +import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; +import { repeat } from 'rxjs/operators'; +import { timer } from 'rxjs/observable/timer'; + +import {Strudelapp} from './strudelapp'; +import { TesService } from './tes.service'; +import { Identity } from './identity'; +import { BatchInterface} from './batchinterface'; + +// This service is responsible for starting applications +// Along the way it opens new windows/tabs to +/** This service is repsonible for starting applications +Along the way it opens new windows/tabs to determine + 1) Any configuration parameters for the application itself (if the app suports This + 2) The batch command to submit the application to a cluster (if any) +The windows are "watched" for URL changes and parameters are extracted from the +query string +*/ + + +@Injectable({ + providedIn: 'root' +}) +export class SubmitAppService { + private configwindow: any; + private appparams: string; + private submitcmd: string; + + + private windowWatcher: any; + private watchingForSubmit: boolean; + + + constructor(private tesService: TesService) { + this.appparams = null; + this.submitcmd = null; + this.watchingForSubmit = false; + } + + public launchApp(app: Strudelapp, identity: Identity) { + if (!(app.url === null) && !(app.url =='')) { + this.openWindow(app,app.url,identity); + } else { + this.appparams = JSON.stringify([]); + let redirect = window.location.toString(); + redirect = redirect.replace(/launch/,'finishlaunch'); + let paramstr = this.buildParams(app,identity,redirect); + let windowloc = identity.site.url+'configure/'+app.name+"?"+paramstr; + this.openWindow(app,windowloc,identity); + } + // this.openLaunchWindow(app, identitiy); + } + + + public openWindow(app: any,url: string, identity: Identity) { + console.log('in openLaunchDialog'); + console.log(app); + + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + let redirect = window.location.toString(); + redirect = redirect.replace(/launch/,'finishlaunch'); + let paramstr = this.buildParams(app,identity,redirect); + this.configwindow = window.open(url); + if (!(this.windowWatcher === null) && !(this.windowWatcher === undefined)) { + this.windowWatcher.unsubscribe() + } + this.windowWatcher = timer(500).pipe(repeat()).subscribe(() => this.watchWindow(this.configwindow, identity, app,redirect)); + this.configwindow.focus(); + + } + + public watchWindow(configwindow, id, app, redirect) { + // Watch another window for url changes + // if the url changes to include the submit parameters, submit the job + // if the url changes to include appparameters, save them and open + // a new window to get the submit parameters + var location: string; + try { + location = configwindow.location.href; + // if we can read the location and it contains a submitcmd arameter save it + // and close the window + if (configwindow.closed) { // if the window is closed without our help, presumable the user wanted to cancel the launch + return + } + let match = location.match(/submitcmd\=([^&]+)(&|$)/); + if (match) { + this.submitcmd = decodeURIComponent(match[1]); + if (!(configwindow.closed)) { + configwindow.close() + } + this.windowWatcher.unsubscribe(); + } + // if we can read the location and it contains an appparams parameter, save it + // and close the window + match = location.match(/appparams\=([^&]+)(&|$)/); + if (match) { + this.appparams = decodeURIComponent(match[1]); + if (!(configwindow.closed)) { + configwindow.close() + } + this.windowWatcher.unsubscribe(); + } + // if we can read the location and it contains the redireURI, close the window + let idx = location.indexOf(redirect); + if (idx == 0) { + configwindow.close(); + } + } catch { + location = null; + } + if (configwindow.closed) { + this.windowWatcher.unsubscribe(); + // if the window is closed, see if we need to override appparams from localStorage + try { + let appparams = localStorage.getItem('appparams'); + if (appparams != null) { + localStorage.removeItem('appparams'); + this.appparams = appparams; + } + } catch { + } + // if the window is closed and we have an appparams value but not a submitcmd + // open a new window to get the submitcmd + + + if (!(this.appparams === null) && (this.submitcmd === null)) { + let paramstr = this.buildParams(app,id,redirect); + let windowloc = id.site.url+'configure/'+app.name+"?"+paramstr; + this.windowWatcher.unsubscribe(); + this.openWindow(app,windowloc,id); + } + // if the window is closed and we have an appparams valud AND a submitcmd + // submit + if (!(this.appparams == null) && !(this.submitcmd == null )) { + + let bi = new BatchInterface(); + bi.submitcmd = this.submitcmd; + this.tesService.submit(app,id,bi,this.appparams); + this.appparams = null; + this.submitcmd = null; + } + } + } + + private buildParams(app: Strudelapp, identity: Identity, redirect): string { + let params = new URLSearchParams(); + let id = identity.copy_skip_catalog(); + id.site.appCatalog = null; + + params.set('app',JSON.stringify(app)); + params.set('identity',JSON.stringify(id)); + params.set('redirect',redirect); + return params.toString(); + } + +} diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.css b/src/app/sv2-side-nav/sv2-side-nav.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.html b/src/app/sv2-side-nav/sv2-side-nav.component.html new file mode 100644 index 0000000..216506d --- /dev/null +++ b/src/app/sv2-side-nav/sv2-side-nav.component.html @@ -0,0 +1,16 @@ +<!-- <mat-accordion> --> +<mat-expansion-panel> + <mat-expansion-panel-header> + <mat-panel-title> + Login + </mat-panel-title> + </mat-expansion-panel-header> + <div *ngFor="let sshauthzserver of (authService.SshAuthzServers | async)"> + <div style="width: 100%"> + <button mat-button (click)=login(sshauthzserver) fxFlex style="text-align: left">Login to {{ sshauthzserver.name }} </button> + <button mat-icon-button *ngIf="sshauthzserver.userdefined === true"><mat-icon>remove</mat-icon></button> + </div> + </div> + <button mat-button (click)=logout() style="width: 100%; text-align: left" >Logout</button> +</mat-expansion-panel> +<!-- </mat-accordion> --> diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts b/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts new file mode 100644 index 0000000..f8efea9 --- /dev/null +++ b/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Sv2SideNavComponent } from './sv2-side-nav.component'; + +describe('Sv2SideNavComponent', () => { + let component: Sv2SideNavComponent; + let fixture: ComponentFixture<Sv2SideNavComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ Sv2SideNavComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(Sv2SideNavComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.ts b/src/app/sv2-side-nav/sv2-side-nav.component.ts new file mode 100644 index 0000000..c36203d --- /dev/null +++ b/src/app/sv2-side-nav/sv2-side-nav.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogConfig } from '@angular/material'; + +import { AuthorisationService } from '../authorisation.service'; +import { LogoutdialogComponent } from '../logoutdialog/logoutdialog.component'; + + +@Component({ + selector: 'app-sv2-side-nav', + templateUrl: './sv2-side-nav.component.html', + styleUrls: ['./sv2-side-nav.component.css'] +}) +export class Sv2SideNavComponent implements OnInit { + + constructor(public authService: AuthorisationService, public dialog: MatDialog,) { } + + ngOnInit() { + console.log('sv2-side-nav init'); + } + logout() { + let dialogRef = this.dialog.open(LogoutdialogComponent, { + width: '250px', + height: '400px', + }); + } + + login (sshauthzserver) { + this.authService.login(sshauthzserver); + } + +} diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts index 2c66105..3ef42fc 100644 --- a/src/app/tes.service.ts +++ b/src/app/tes.service.ts @@ -7,7 +7,7 @@ import { catchError, map, tap } from 'rxjs/operators'; import { Job } from './job'; import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import { Subject } from 'rxjs/Subject'; -import { Strudelapp } from './strudelapp'; +import { Strudelapp, StrudelappInstance } from './strudelapp'; import { Computesite } from './computesite'; import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity'; import { BatchInterface} from './batchinterface'; @@ -15,12 +15,15 @@ import {ComputesitesService} from './computesites.service'; import { StrudelappsService } from './strudelapps.service'; import { timer } from 'rxjs/observable/timer'; import { repeat } from 'rxjs/operators'; -import {LocationStrategy, Location} from '@angular/common'; +import { LocationStrategy, Location } from '@angular/common'; import { ActivatedRoute, Router } from '@angular/router'; import { ModaldialogComponent } from './modaldialog/modaldialog.component'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material'; -import { MatSnackBar } from '@angular/material'; +import { AuthorisationService } from './authorisation.service'; +import { environment } from '../environments/environment'; +/** The TES service contains ways to start Tunnels, and Execute programs +Its also responsible for querying a compute site for running jobs */ @@ -28,56 +31,48 @@ import { MatSnackBar } from '@angular/material'; providedIn: 'root', }) export class TesService { -// public Base='https://vm-118-138-240-255.erc.monash.edu.au/tes' -public Base='http://localhost:5000'; +public Base=environment.tesurl; +// public Base='http://localhost:5000'; public statusMsg: BehaviorSubject<any>; public jobs: any[]; public busy: BehaviorSubject<boolean> ; -public joblist: BehaviorSubject<{ [id: string ]: Job[]}>; +// public joblist: BehaviorSubject<{ [id: string ]: Job[]}>; +public joblist: BehaviorSubject<Job[]>; + private timerSubscription: any; -// public keyCert: Subject<KeyCert>; -public identities: BehaviorSubject<Identity[]>; private appwindow: any; private appwindowWatcher: any; public batchinterface: {[id: string] : BatchInterface}; -private snackBarRef: any; constructor(private http: HttpClient, public dialog: MatDialog, - private computesite: ComputesitesService, + private computesitesService: ComputesitesService, + private authorisationService: AuthorisationService, private strudelappsService: StrudelappsService, - public snackBar: MatSnackBar, private location: Location ) { - this.statusMsg = new BehaviorSubject<any>(''); - this.statusMsg.subscribe(msg => this.displayMessage(msg)); + this.busy = new BehaviorSubject<boolean>(false); - this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({}); + // this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({}); + this.joblist = new BehaviorSubject<Job[]>([]); + this.timerSubscription = null; this.appwindowWatcher = null; - // this.keyCert = new Subject<KeyCert>(); - this.identities= new BehaviorSubject<Identity[]>([]); - // this.keyCert.subscribe(keyCert => this.sshAdd(keyCert)); this.batchinterface = {}; - // this.getIdentities(); + // this.computesitesService.identities.subscribe(identities => this.startPolling(identities)); } - private displayMessage(msg) { - if (msg === null || msg == "") { - console.log('attempting to dismis snackbar'); - if (this.snackBarRef != undefined) { - this.snackBarRef.dismiss() - } - } else { - this.snackBarRef = this.snackBar.open(msg,'Dismiss'); - } - } +public setStatusMsg(statusMsg: BehaviorSubject<any>) { + this.statusMsg = statusMsg; +} - private buildParams(app: Strudelapp, identity: Identity, batchinterface: BatchInterface): string { + private buildParams(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, appinst?: any): string { let params = new URLSearchParams(); - let params2 = new URLSearchParams(); let id = identity.copy_skip_catalog(); id.site.appCatalog = null; + if (appinst!== null) { + params.set('appinstance',JSON.stringify(appinst)); + } params.set('app',JSON.stringify(app)); params.set('interface',JSON.stringify(batchinterface)); params.set('identity',JSON.stringify(id)); @@ -92,78 +87,84 @@ private snackBarRef: any; j.app = this.strudelappsService.getApp(j.name,identity.site.appCatalog.value); j.identity = identity; } - alljobs[identity.repr()] = joblist; - this.joblist.next(alljobs); + this.joblist.next(joblist) + // alljobs[identity.repr()] = joblist; + // this.joblist.next(alljobs); } - getJobs() { - let headers = new HttpHeaders(); - let options = { headers: headers, withCredentials: true}; - // remove from the job list any jobs for identities that we don't know about - let oldjobs = this.joblist.value; - let oldjobkeys = Object.keys(oldjobs); - let ids = [] - for (let identity of this.identities.value) { - ids.push(identity.repr()); - } - for (let id of oldjobkeys) { - if (!ids.includes(id)) { - delete oldjobs[id]; - } - } - this.joblist.next(oldjobs); - // for identities that we do know about, query the job list - for (let identity of this.identities.value) { - if (this.batchinterface[identity.repr()] === undefined) { - this.getconfig(new Strudelapp(),identity) - .pipe(catchError(this.handleError)) - .subscribe(resp => this.batchinterface[identity.repr()] = resp, - error => this.httperror(error)); - continue; - } - let paramstr = this.buildParams(null,identity,this.batchinterface[identity.repr()]); - this.http.get<Job[]>(this.Base+'/stat'+'?'+paramstr,options) - .pipe(catchError(this.handleError)) - .subscribe(resp => this.updateJoblist(resp,identity), - error => this.httperror(error)); - } + private getBatchInterfaceError(error: any) { + console.log('in getBatchInterfaceError'); + console.error(error); } - private startPolling() { - if (!(this.timerSubscription === null)) { - this.timerSubscription.unsubscribe() + private getJobsError(error: any) { + this.joblist.next([]); + if (error.status == 0) { + this.statusMsg.next("A network error occured. Please try again latter"); + return } - this.timerSubscription = timer(5000).pipe(repeat()).subscribe(() => this.getJobs()); - this.getJobs(); - } - - private stopPolling() { - if (!(this.timerSubscription === null)) { - this.timerSubscription.unsubscribe() + console.log(error); + if (error.status == 404) { + this.statusMsg.next("Login expired. Please log in again."); + return } } + getJobs(identity: Identity) { + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + // remove from the job list any jobs for identities that we don't know about + let oldjobs = this.joblist.value; + if ( identity === undefined) { + this.joblist.next([]); + return + } + if (this.batchinterface[identity.repr()] === undefined) { + this.getconfig(new Strudelapp(),identity) + // .pipe(catchError(this.handleError)) + .subscribe(resp => { this.batchinterface[identity.repr()] = resp; this.getJobs(identity) }, + error => this.getBatchInterfaceError(error)); + // error => this.getJobsError(error,identity)); + + + } else { + let paramstr = this.buildParams(null,identity,this.batchinterface[identity.repr()]); + this.http.get<Job[]>(this.Base+'/stat'+'?'+paramstr,options) + // .pipe(catchError(this.networkError)) + .subscribe(resp => this.updateJoblist(resp, identity), + error => this.getJobsError(error)); + } +} + public getconfig(app: Strudelapp, identity: Identity): Observable<any> { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; return this.http.get<any>(identity.site.url+'getconfig/'+app.name,options) - .pipe(catchError(this.handleError)) + // .pipe(catchError(this.handleError)) } - submissionError(message: string) { - this.statusMsg.next(message); - this.busy.next(false); - console.log(message); + submissionError(error: any) { + if (error.status != 0) { + this.statusMsg.next(error.error.message); + this.busy.next(false); + } + } + + buildBody(app: Strudelapp, appparams?: string) { + return JSON.stringify({'app': app, 'appparams': appparams}); } - submit(app: Strudelapp, identity: Identity, batchinterface: BatchInterface) { + submit(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, appparams?: string) { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; this.statusMsg.next('Submitting job'); this.busy.next(true); let paramstr = this.buildParams(app,identity,batchinterface); - this.http.post<any>(this.Base+'/submit'+'?'+paramstr,{}, options) - .pipe(catchError(this.handleError)) + // let body = this.buildBody(app,appparams) + let keys = this.authorisationService.getKeys(); + let body = {'app': app, 'appparams': appparams, 'keys': keys} + this.http.post<any>(this.Base+'/submit'+'?'+paramstr, body, options) + // .pipe(catchError(this.handleError)) .subscribe(resp => { this.busy.next(false); this.statusMsg.next(null) @@ -171,10 +172,10 @@ private snackBarRef: any; error => this.submissionError(error)); } - submitted(resp: any ) { + submitted(resp: any, identity: Identity ) { this.busy.next(false); // this.statusMsg.next(null); - this.getJobs(); + this.getJobs(identity); } cancel(job: Job) { @@ -185,7 +186,7 @@ private snackBarRef: any; let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]); this.http.delete<any>(this.Base+'/cancel/'+job.jobid+'?'+paramstr, options) .pipe(catchError(this.handleError)) - .subscribe(resp => this.submitted(resp)); + .subscribe(resp => this.submitted(resp,job.identity)); } public watchAppwindow(appwindow, dialogRef) { @@ -194,17 +195,63 @@ private snackBarRef: any; } } - public connect(job: Job) { - // this.statusMsg.next(null); + public getAppInstance(job: Job) { + let username = job.identity.username; + let loginhost = job.identity.site.host; + let batchhost = job.batch_host; + let params = new URLSearchParams; let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; - this.busy.next(true); - let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]); - // let appwindow = window.open(this.Base+"/connect/"+job.jobid+"/"+job.batch_host+"?"+paramstr); + params.set('cmd',JSON.stringify(job.app.paramscmd)); + let paramstr = params.toString(); + job.connectionState = 1; + this.http.get<string>(this.Base+'/appinstance/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, options) + // .pipe(catchError(this.handleError)) + .subscribe(resp => { job.appinst = resp; this.createTunnel(job) ; console.log('got app inst data'+job.appinst)} ) + // let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]); + // let headers = new HttpHeaders(); + // let options = { headers: headers, withCredentials: true}; + // this.http.get<any>(this.Base+'/getappinst/'+paramstr,options) + // .pipe(catchError(this.handleError)) + // .subscribe(resp => { job.appinst = <StrudelappInstance>resp; this.openAppWindow(job);}); + } + + public createTunnel(job: Job) { + let username = job.identity.username; + let loginhost = job.identity.site.host; + let batchhost = job.batch_host; + let params = new URLSearchParams; + let headers = new HttpHeaders(); + job.connectionState = 2; + let options = { headers: headers, withCredentials: true}; + params.set('cmd',JSON.stringify(job.app.paramscmd)); + let paramstr = params.toString(); + this.http.post<string>(this.Base+'/createtunnel/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, job.appinst, options) + // .pipe(catchError(this.handleError)) + .subscribe(() => { this.getAppUrl(job) } ) + } - // let windowloc = this.Base+"/connect/"+job.jobid+"/"+job.batch_host+"?"+paramstr; - let url = this.Base+"/connect/"+job.jobid+"/"+job.batch_host; - let windowloc = './connecting?connecturl='+url+"&"+paramstr; + public getAppUrl(job: Job) { + let params = new URLSearchParams; + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + params.set('app',JSON.stringify(job.app)); + params.set('appinst',JSON.stringify(job.appinst)); + let paramstr = params.toString(); + job.connectionState = 3; + this.http.get<string>(this.Base+'/appurl?'+paramstr,options) + .pipe(catchError(this.handleError)) + .subscribe(resp => { console.log(resp); job.connectionState = 0; this.openAppWindow(resp)}); + + + } + + public getInterface(job: Job) { + this.getAppInstance(job); + } + + public openAppWindow(url: string) { + let windowloc = url; // let windowloc = this.router.config this.appwindow = window.open(windowloc); if (!(this.appwindowWatcher === null)) { @@ -217,77 +264,57 @@ private snackBarRef: any; this.appwindowWatcher = timer(500).pipe(repeat()).subscribe(() => this.watchAppwindow(this.appwindow,dialogRef)); // this.appwindow.location.assign(windowloc); this.appwindow.focus(); - this.busy.next(false); } + public connect(job: Job, appinst?: any) { + // this.statusMsg.next(null); + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + // this.busy.next(true); + this.getInterface(job); // getInterface will subsequently called getAppInstance, which will call createTunnel, which will openAppWindow + } -public sshAdd(keyCert: KeyCert) { - if (keyCert.key == undefined) { - return; - } + + + +public postAgentData(keyCert: KeyCert) { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; this.statusMsg.next("Authorising ...") let data = {'key': keyCert.key, 'cert': keyCert.cert}; - this.http.post<any>(this.Base+'/sshagent',data,options) + return this.http.post<any>(this.Base+'/sshagent',data,options) .pipe(catchError(this.handleError)) - .subscribe(resp => this.getIdentities(), - error => this.httperror(error)) + } -public getIdentities() { - this.statusMsg.next("Updating the list of available accounts"); +public postMkDir(id: Identity, path: string, name: string ) { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; - this.http.get<any>(this.Base+'/sshagent',options) - .pipe(catchError(this.handleError)) - .subscribe(resp => this.updateIdentities(resp), - error => this.httperror(error)) + let params = new URLSearchParams; + params.set('identity',JSON.stringify(id)); + params.set('path',JSON.stringify(path)); + + return this.http.post<any>(this.Base+'/mkdir?'+params.toString(),name, options) + .pipe(catchError(this.handleError)) + } -private killAgent() { - this.statusMsg.next("Updating the list of available accounts") +public getSftpData(id: Identity, path: string, cd: string ) { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; - this.http.delete<any>(this.Base+'/sshagent',options) - .pipe(catchError(this.handleError)) - .subscribe(resp => this.updateIdentities(resp), - error => this.httperror(error)); -} + let params = new URLSearchParams; + params.set('identity',JSON.stringify(id)); + params.set('path',JSON.stringify(path)); + params.set('cd',JSON.stringify(cd)); -private updateIdentities(resp) { - let certcontents = resp; - let identities = []; - var cs: any; - let idsShort = [] - for (cs of this.computesite.returnComputeSites()) { - let computesite = <Computesite>cs; - for (let i in certcontents) { - var fp: string = (<Computesite>cs).cafingerprint; - for (let ca of certcontents[i]['Signing CA']) { - if (ca == fp) { - identities.push(new Identity(certcontents[i].Principals[0],<Computesite>cs)) - } - } - } - } - this.identities.next(identities); - this.statusMsg.next(null); - if (identities.length == 0) { - return; - } - this.startPolling(); -} + return this.http.get<any>(this.Base+'/ls?'+params.toString(),options) + .pipe(catchError(this.handleError)) -public logout(): Boolean { - for (let id of this.identities.value) { - console.log('logout identitiy',id.authservice); - } - this.killAgent(); - return true; } + + public getstatusMsgSubject(): BehaviorSubject<any> { return this.statusMsg; } @@ -297,11 +324,20 @@ private httperror(errorstr: string) { let searchresult = errorstr.search(re); if (searchresult != -1) { this.statusMsg.next("Some authentication tokens have expired, you may need to log in again"); - this.getIdentities(); + this.authorisationService.updateAgentContents(); } console.log(errorstr); } +// private networkError(error: HttpErrorResponse) { +// if (error.error instanceof ErrorEvent) { +// console.log('network error contacting TES backend'); +// } else { +// return error; +// } +// } + + private handleError(error: HttpErrorResponse) { if (error.error instanceof ErrorEvent) { console.log('instance of ErrorEvent'); @@ -315,12 +351,16 @@ private handleError(error: HttpErrorResponse) { // that the user isn't allowed to run the job var re = /identity/gi; - let searchresult = error.error.message.search(re); - if (searchresult != -1) { - // this.getIdentities(); - return throwError('login expired, refreshing'); + if (error.error.message != undefined) { + let searchresult = error.error.message.search(re); + if (searchresult != -1) { + // this.getIdentities(); + return throwError('login expired, refreshing'); + } + return throwError(error.error.message) + } else { + return throwError('An error occured but I\'m not sure exactly what. Please try again latter or contact the sys admins'); } - return throwError(error.error.message) // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message); } diff --git a/src/app/teserrors/teserrors.component.html b/src/app/teserrors/teserrors.component.html deleted file mode 100644 index 7899878..0000000 --- a/src/app/teserrors/teserrors.component.html +++ /dev/null @@ -1,3 +0,0 @@ -<!-- <p> -{{ statusMsg }} -</p> --> diff --git a/src/app/teserrors/teserrors.component.ts b/src/app/teserrors/teserrors.component.ts deleted file mode 100644 index 386fc59..0000000 --- a/src/app/teserrors/teserrors.component.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { TesService } from '../tes.service'; -import { MatSnackBar } from '@angular/material'; - - -@Component({ - selector: 'app-teserrors', - templateUrl: './teserrors.component.html', - styleUrls: ['./teserrors.component.css'] -}) -export class TeserrorsComponent implements OnInit { - - // public statusMsg: string; - private snackBarRef: any; - constructor(private tesService: TesService, public snackBar: MatSnackBar,) { } - ngOnInit() { - // this.tesService.getstatusMsgSubject().subscribe( msg => this.getNewMessage(msg)); - } - - getNewMessage(msg) { - if (msg === null) { - console.log('attempting to dismis snackbar'); - if (this.snackBarRef != undefined) { - this.snackBarRef.dismiss() - } - } else { - this.snackBarRef = this.snackBar.open(msg,'Dismiss'); - } - } - -} diff --git a/src/app/transfer/transfer.component.css b/src/app/transfer/transfer.component.css new file mode 100644 index 0000000..683ae23 --- /dev/null +++ b/src/app/transfer/transfer.component.css @@ -0,0 +1,3 @@ +:host { + height: 100%; +} diff --git a/src/app/transfer/transfer.component.html b/src/app/transfer/transfer.component.html new file mode 100644 index 0000000..4259e24 --- /dev/null +++ b/src/app/transfer/transfer.component.html @@ -0,0 +1,54 @@ +<div fxFlex style="flex: 1 1 0%; box-sizing: border-box"> +<div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" > + <mat-toolbar color="primary"> + <mat-toolbar-row> + <button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button> + + <span>Strudel v2.0</span> + <span class="fill-horizontal-space"></span> + </mat-toolbar-row> + </mat-toolbar> + +<mat-sidenav-container #idSideNav autosize style="height: 100%; width: 100%"> + <mat-sidenav #idSideNav mode="side" opened> + <app-sv2-side-nav></app-sv2-side-nav> + </mat-sidenav> + <div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" > + + <div fxLayout="row" fxLayout="space-between stretch" fxLayoutGap="1%" style="height: 90%"> + <!-- <div fxFlex *ngFor="let idx of [0,1]" style="height: 100%; overflow-y: auto"> --> + <div fxFlex *ngFor="let idx of [0,1]" style="height: 100%"> + + <!-- <div style="height: 800px; background-color: #00ffff; overflow-y: scroll"></div> --> + + <mat-form-field> + <mat-select (selectionChange)="setId(idx,$event)" placeholder="Choose a server"> + <mat-option *ngFor="let s of computesitesService.ftidentities | async" [value]="s"> + {{ s.username }}@{{s.site.name }} + </mat-option> + </mat-select> + </mat-form-field> + <span class="mat-body"> {{ path[idx] }}</span> + <div *ngIf="working[idx] | async"> + <mat-spinner></mat-spinner> + </div> + <div *ngIf="!(working[idx] | async)" style="height: 90%; overflow-y: auto"> + <file-explorer [fileElements]="fileElements[idx] | async" + [path]="pathSubject[idx] | async" + (navigatedDown)="navigateToFolder(idx,$event)" + (navigatedUp)="navigateUp(idx,$event)" + [canNavigateUp]="canNavigateUp[idx] | async" + (sendFile)="sendFile(idx,$event)" + (folderAdded)="newDirectory(idx,$event)" > + </file-explorer> + </div> + </div> + </div> + </div> + + + + <!-- <mat-card style="width: 100%; box-sizing: border-box">Transfer tasks</mat-card> --> +</mat-sidenav-container> +</div> +</div> diff --git a/src/app/transfer/transfer.component.spec.ts b/src/app/transfer/transfer.component.spec.ts new file mode 100644 index 0000000..73f4b41 --- /dev/null +++ b/src/app/transfer/transfer.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TransferComponent } from './transfer.component'; + +describe('TransferComponent', () => { + let component: TransferComponent; + let fixture: ComponentFixture<TransferComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TransferComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TransferComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/transfer/transfer.component.ts b/src/app/transfer/transfer.component.ts new file mode 100644 index 0000000..66a33ab --- /dev/null +++ b/src/app/transfer/transfer.component.ts @@ -0,0 +1,175 @@ +import { Component, OnInit } from '@angular/core'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Observable } from 'rxjs/Observable'; +import { FileElement } from '../file-explorer/model/element'; +import { TesService } from '../tes.service'; +import { ComputesitesService } from '../computesites.service'; +import { AuthorisationService } from '../authorisation.service'; +import { Computesite } from '../computesite'; +import { Identity } from '../identity'; +import { Router } from '@angular/router'; + + + + + +@Component({ + selector: 'app-transfer', + templateUrl: './transfer.component.html', + styleUrls: ['./transfer.component.css'] +}) +export class TransferComponent implements OnInit { + + fileElements: BehaviorSubject<FileElement[]>[]; + pathSubject: BehaviorSubject<string>[]; + path: string[]; + working: BehaviorSubject<Boolean>[]; + canNavigateUp: BehaviorSubject<Boolean>[]; + id: Identity[]; + site: Computesite[]; + disableSend: Boolean; + statusMsg: BehaviorSubject<string>; + + + constructor(private computesitesService: ComputesitesService, + private tesService: TesService, + private authService: AuthorisationService, + private router: Router) { + this.path = ['','']; + this.fileElements = [new BehaviorSubject([]), new BehaviorSubject([])]; + this.working = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; + this.canNavigateUp = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; + this.pathSubject = [ new BehaviorSubject(""),new BehaviorSubject("")]; + this.id = [null,null]; + this.disableSend = true; + } + + ngOnInit() { + var fel: FileElement[] = [] + // this.fileElements = [new BehaviorSubject([]), new BehaviorSubject([])]; + // this.working = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; + // this.canNavigateUp = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; + // this.pathSubject = [ new BehaviorSubject(""),new BehaviorSubject("")]; + // this.id = [null,null]; + // this.disableSend = true; + setTimeout( () => this.authService.updateAgentContents() ) + + } + +compareObjects(o1: any, o2: any) { + if(o1.name == o2.name) { + return true; + } else { + return false; + } +} + +newDirectory(idx: number, name: string) { + console.log('attempt to create directory',name,'on',this.id[idx]); + this.working[idx].next(true); + this.tesService.postMkDir(this.id[idx],this.path[idx],name).subscribe(resp => this. getSftpls(idx,'.')); +} + +getSftpls(idx,cd) { + console.log('getSftpls'); + console.log(this.id); + console.log(idx); + this.working[idx].next(true); + this.tesService.getSftpData(this.id[idx],this.path[idx],cd).subscribe(resp => this.updateSftpls(idx,resp)); +} + +makeFileElements(data) { + console.log('making file elements'); + console.log(data); + var rv: FileElement[] = []; + var fe: FileElement; + for (let f of data) { + fe = new FileElement(); + if (f.isdir !== undefined) { + fe.isFolder = f.isdir; + } else { + if (f.mode.indexOf('d')== 0) { + fe.isFolder=true; + } + } + fe.name = f.name; + fe.size = f.size; + fe.online = f.online; + rv.push(fe); + } + return rv; +} + + updateSftpls(idx,resp) { + let files = this.makeFileElements(resp.files); + console.log(resp); + console.log('got the current path',resp.pwd); + console.log('got a list files', resp.files); + if (resp.pwd.length > 1) { + this.canNavigateUp[idx].next(true); + } else { + this.canNavigateUp[idx].next(false); + + } + this.path[idx] = resp.pwd; + console.log('full uri',this.id[idx].username+"@"+this.id[idx].site.name+":"+this.path[idx]); + this.pathSubject[idx].next(this.path[idx]); + this.fileElements[idx].next(files); + this.working[idx].next(false); + + } + + + setId(idx,event) { + console.log('setid'); + console.log(event); + this.id[idx] = event.value; + if ((this.id[0] != null) && (this.id[1] != null)) { + console.log('enabling the send function, two ids are selected'); + this.disableSend = false; + } + this.path[idx]="."; + this.pathSubject[idx].next(this.id[idx].username+"@"+this.id[idx].site.name+":"+this.path[idx]); + this.getSftpls(idx,'.'); + + } + + navLaunch() { + console.log('attempting navLaunch'); + this.router.navigate(['/launch']); + } + + navigateToFolder(idx,element: FileElement) { + this.getSftpls(idx,element.name); + } + + navigateUp(idx: number, event: any) { + console.log('attempting navigate up',idx); + this.getSftpls(idx,'..'); + } + + + + sendFile(idx: number, element: FileElement) { + let src = idx; + let dest = (idx+1) % 2; + this.id[src].keyCerts = this.authService.getKeys(this.id[src]); + this.id[dest].keyCerts = this.authService.getKeys(this.id[dest]); + if ((this.id[src] != null) && (this.id[dest] != null)) { + let appparams = JSON.stringify( + {'src': { + 'id': this.id[src], + 'path': this.path[src], + 'file': element.name }, + 'dest': { + 'id': this.id[dest], + 'path': this.path[dest] + }}); + localStorage.setItem('appparams',appparams); + window.close(); + } else { + console.log('Dont send file not enough servers'+element.name+' on '); + } + } + +} diff --git a/src/assets/config/authservers.json b/src/assets/config/authservers.json new file mode 100644 index 0000000..376edea --- /dev/null +++ b/src/assets/config/authservers.json @@ -0,0 +1,34 @@ +[ + + { + "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/aafcentral", + "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/", + "client_id": "Q96kt2Vtw6S78dpORktM81DH", + "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/monash_hpcid/api/v1/sign_key", + "logout": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/logout", + "name": "M3 via AAF Central", + "icon": null, + "scope": "user:email" + }, + { + "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/google", + "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/", + "client_id": "Q96kt2Vtw6S78dpORktM81DH", + "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/monash_hpcid/api/v1/sign_key", + "logout": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/logout", + "name": "M3 via Google", + "icon": null, + "scope": "user:email" + }, + { + "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/aafcentral", + "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/", + "client_id": "Q96kt2Vtw6S78dpORktM81DH", + "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/swinburne/api/v1/sign_key", + "logout": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/logout", + "name": "OzStar via AAF Central", + "icon": null, + "scope": "user:email" + } + +] diff --git a/src/assets/config/computesites.json b/src/assets/config/computesites.json new file mode 100644 index 0000000..ee5d818 --- /dev/null +++ b/src/assets/config/computesites.json @@ -0,0 +1,40 @@ +[ + { + "url": "http://localhost:8080/", + "name": "M3", + "host": "m3-login2.massive.org.au", + "dtn": "m3-dtn1.massive.org.au", + "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I", + "appCatalog": [], + "appCatalogUri": "./assets/config/m3apps.json" + }, + { + "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3siteconfig/", + "name": "OzStar", + "host": "ozstar.swin.edu.au", + "dtn": "ozstar.swin.edu.au", + "cafingerprint": "RSA SHA256:uRYwTJSLksZbwSXYYmOjXh6JzSXpwVnY6o7BYFJc60g", + "appCatalog": [], + "appCatalogUri": "./assets/config/m3apps.json" + }, + { + "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3siteconfig_dev/", + "name": "CVL@UWA", + "host": "146.118.65.246", + "dtn": "146.118.65.246", + "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I", + "appCatalog": [], + "appCatalogUri": "./assets/config/cvluwaapps.json" + }, + { + "url": null , + "name": "Vault-V2@Monash", + "host": "dtn-2.erc.monash.edu.au", + "dtn": "dtn-2.erc.monash.edu.au", + "lscmd": "/srv/home/chines/hsmls.py", + "dtnport": "33001", + "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I", + "appCatalog": [], + "appCatalogUri": null + } +] diff --git a/src/assets/config/computesites.json~ b/src/assets/config/computesites.json~ new file mode 100644 index 0000000..11ff902 --- /dev/null +++ b/src/assets/config/computesites.json~ @@ -0,0 +1,26 @@ +[ + { + "url": "http://localhost:8080/", + "name": "M3", + "host": "m3-login2.massive.org.au", + "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I", + "appCatalog": [], + "appCatalogUri": "assets/config/m3apps.json" + }, + { + "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3staticsiteconfig/", + "name": "M3 Static Services", + "host": "m3-login2.massive.org.au", + "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I", + "appCatalog": [], + "appCatalogUri": "assets/config/m3staticapps.json" + }, + { + "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3siteconfig/", + "name": "OzStar", + "host": "ozstar.swin.edu.au", + "cafingerprint": "RSA SHA256:uRYwTJSLksZbwSXYYmOjXh6JzSXpwVnY6o7BYFJc60g", + "appCatalog": [], + "appCatalogUri": "assets/config/m3apps.json" + } +] diff --git a/src/assets/config/cvluwaapps.json b/src/assets/config/cvluwaapps.json new file mode 100644 index 0000000..e97e7c9 --- /dev/null +++ b/src/assets/config/cvluwaapps.json @@ -0,0 +1,62 @@ +[ + { "url": null, + "name": "Desktop", + "startscript": "#!/bin/bash\n/home/chines/sv2/desktop_novnc/desktop.sh\n ", + "paramscmd": "/home/chines/sv2/desktop_novnc/params.py", + "client": {"cmd": null, "redir": "vnc.html?host=vm-118-138-240-255.erc.monash.edu.au&port=443&password={password}" }, + "localbind": true, + "applist": null + }, + { "url": null, + "name": "Guacamole Desktop", + "startscript": "#!/bin/bash\n/home/chines/desktop.sh\n ", + "paramscmd": "/home/chines/vdiparam.py", + "client": {"cmd": null, "redir": "guacamole?username={username}&password={password}" }, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "Jupyter Lab", + "startscript": "#!/bin/bash\n/home/chines/jupyter.slurm\n", + "paramscmd": "/home/chines/jupyter_params.py", + "client": {"cmd": null, "redir": "?token={token}"}, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "LiberTEM", + "startscript": "eval $(cat /home/chines/libertembatch)", + "paramscmd": "/projects/pMOSP/chines/libertem_venv/bin/python /projects/pMOSP/chines/libertem_venv/bin/server_stat.py", + "client": {"cmd": null, "redir": ""}, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "CryoSPARC", + "startscript": null, + "paramscmd": "/home/chines/cryosparc_params.py", + "client": {"cmd": null, "redir": ""}, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "R Studio Server", + "startscript": "#!/bin/bash\n/usr/local/sv2/rstudioserver/run.sh\n", + "paramscmd": "/usr/local/sv2/rstudioserver/params.py", + "client": {"cmd": null, "redir": ""}, + "localbind": true, + "applist": null + }, + { "url": "transfer", + "name": "Transfer files", + "startscript": "#!/bin/bash\n echo '{appparams}' > ft.json", + "paramscmd": "/home/chines/sv2/desktop_novnc/params.py", + "client": {"cmd": null, "redir": null }, + "localbind": true, + "applist": null + } +] diff --git a/src/assets/config/m3apps.json b/src/assets/config/m3apps.json new file mode 100644 index 0000000..e97e7c9 --- /dev/null +++ b/src/assets/config/m3apps.json @@ -0,0 +1,62 @@ +[ + { "url": null, + "name": "Desktop", + "startscript": "#!/bin/bash\n/home/chines/sv2/desktop_novnc/desktop.sh\n ", + "paramscmd": "/home/chines/sv2/desktop_novnc/params.py", + "client": {"cmd": null, "redir": "vnc.html?host=vm-118-138-240-255.erc.monash.edu.au&port=443&password={password}" }, + "localbind": true, + "applist": null + }, + { "url": null, + "name": "Guacamole Desktop", + "startscript": "#!/bin/bash\n/home/chines/desktop.sh\n ", + "paramscmd": "/home/chines/vdiparam.py", + "client": {"cmd": null, "redir": "guacamole?username={username}&password={password}" }, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "Jupyter Lab", + "startscript": "#!/bin/bash\n/home/chines/jupyter.slurm\n", + "paramscmd": "/home/chines/jupyter_params.py", + "client": {"cmd": null, "redir": "?token={token}"}, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "LiberTEM", + "startscript": "eval $(cat /home/chines/libertembatch)", + "paramscmd": "/projects/pMOSP/chines/libertem_venv/bin/python /projects/pMOSP/chines/libertem_venv/bin/server_stat.py", + "client": {"cmd": null, "redir": ""}, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "CryoSPARC", + "startscript": null, + "paramscmd": "/home/chines/cryosparc_params.py", + "client": {"cmd": null, "redir": ""}, + "localbind": true, + "applist": null + }, + { + "url": null, + "name": "R Studio Server", + "startscript": "#!/bin/bash\n/usr/local/sv2/rstudioserver/run.sh\n", + "paramscmd": "/usr/local/sv2/rstudioserver/params.py", + "client": {"cmd": null, "redir": ""}, + "localbind": true, + "applist": null + }, + { "url": "transfer", + "name": "Transfer files", + "startscript": "#!/bin/bash\n echo '{appparams}' > ft.json", + "paramscmd": "/home/chines/sv2/desktop_novnc/params.py", + "client": {"cmd": null, "redir": null }, + "localbind": true, + "applist": null + } +] diff --git a/src/assets/config/m3staticapps.json b/src/assets/config/m3staticapps.json new file mode 100644 index 0000000..c784493 --- /dev/null +++ b/src/assets/config/m3staticapps.json @@ -0,0 +1,10 @@ +[ + { + "url": null, + "name": "CryoSPARC", + "startscript": null, + "paramscmd": "/home/chines/cryosparc_params.py", + "client": {"cmd": null, "redir": ""}, + "localbind": true + } +] diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 3612073..358d47b 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,3 +1,5 @@ export const environment = { - production: true + production: true, + tesurl: "https://vm-118-138-240-255.erc.monash.edu.au/tes" + }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index b7f639a..39a4192 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,5 +4,6 @@ // The list of which env maps to which file can be found in `.angular-cli.json`. export const environment = { - production: false + production: false, + tesurl: "http://localhost:5000" }; diff --git a/src/index.html b/src/index.html index 46ca962..c1a1056 100644 --- a/src/index.html +++ b/src/index.html @@ -8,7 +8,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> -<body> - <app-root></app-root> +<body style="height: 100vh; margin-left: 0px; margin-right: 0px; margin-top: 0px; margin-bottom: 0px "> + <app-root></app-root> </body> </html> -- GitLab