Compare commits

..

246 Commits

Author SHA1 Message Date
a9314b65f8 Release v2.2.49 2024-08-06 12:36:04 +02:00
e5b3a101b8 Fix CVE-2024-6345 and CVE-2024-39689. Fixes #1521 #1520 2024-07-22 19:13:46 +02:00
9a2e06471c Development on 2.2.49.dev1 2024-07-13 16:33:10 +02:00
4d695274bb Release v2.2.48.1 2024-07-11 23:40:15 +02:00
e5af89821d Show current year in default-layout.component.html 2024-07-11 23:39:53 +02:00
78b910504d Fix syntax error in index.html. Ref https://github.com/GNS3/gns3-server/issues/2399 2024-07-11 23:21:44 +02:00
27446f8d14 Development on 2.2.49.dev1 2024-07-09 00:20:28 +02:00
a026374e75 Release v2.2.48 2024-07-08 18:21:21 +02:00
4f303921c6 Merge branch 'master' into 2.2 2024-07-08 18:19:14 +02:00
dea6a5021d Merge pull request #1511 from GNS3/dependabot/npm_and_yarn/socket.io-4.7.5
Bump socket.io from 4.4.1 to 4.7.5
2024-06-19 19:02:52 +02:00
46c7c58402 Bump socket.io from 4.4.1 to 4.7.5
Bumps [socket.io](https://github.com/socketio/socket.io) from 4.4.1 to 4.7.5.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/4.4.1...4.7.5)

---
updated-dependencies:
- dependency-name: socket.io
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-19 16:25:06 +00:00
0df3525bbf Merge pull request #1509 from GNS3/dependabot/npm_and_yarn/ws-6.2.3
Bump ws from 6.2.2 to 6.2.3
2024-06-18 20:06:15 +02:00
9667e2363c Bump ws from 6.2.2 to 6.2.3
Bumps [ws](https://github.com/websockets/ws) from 6.2.2 to 6.2.3.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/6.2.2...6.2.3)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-18 16:17:11 +00:00
24ec96a2dd Change interface label to fit in the SVG rect. Ref #1504 2024-06-17 17:37:07 +02:00
9844a2f88f Disable AdButler 2024-06-17 17:16:33 +02:00
0cdbeb98a2 Update Google Analytics ID 2024-06-17 16:18:14 +02:00
dc40d3be6e Merge pull request #1502 from GNS3/dependabot/pip/scripts/requests-2.32.0
Bump requests from 2.31.0 to 2.32.0 in /scripts
2024-05-21 13:46:33 +07:00
2015917767 ---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 05:25:06 +00:00
360d77d2ef Add Qemu for auxiliary console support. 2024-05-16 17:14:13 +07:00
75c3d8ed97 Merge pull request #1500 from GNS3/release/v2.2.47
Release v2.2.47
2024-05-15 16:44:26 +07:00
f94d59206e Development on 2.2.48.dev1 2024-05-15 16:28:14 +07:00
5b41f9789a Release v2.2.47 2024-05-15 11:51:18 +07:00
ac84106dbc Merge pull request #1499 from GNS3/feature/aux-console
Auxiliary console support
2024-05-14 22:17:16 +07:00
21f2267960 Merge remote-tracking branch 'origin/feature/aux-console' into feature/aux-console 2024-05-14 22:12:01 +07:00
a9781943d5 Check auxiliary console port is defined 2024-05-14 22:11:38 +07:00
6fc2f6f964 Merge branch '2.2' into feature/aux-console 2024-05-14 20:19:59 +07:00
cd5773e58a Merge branch 'master' into 2.2 2024-05-14 20:18:30 +07:00
f4bcb844dc Merge pull request #1494
Bump @angular/core from 9.0.0 to 12.2.17
2024-05-14 20:18:10 +07:00
24cf0f5623 Merge pull request #1489 from GNS3/dependabot/npm_and_yarn/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6
2024-05-14 20:10:14 +07:00
5e02a3d757 Merge pull request #1490 from GNS3/dependabot/npm_and_yarn/express-4.19.2
Bump express from 4.18.2 to 4.19.2
2024-05-14 20:09:48 +07:00
42599dafb5 Auxiliary console support 2024-05-14 20:07:30 +07:00
9c754f3444 Merge remote-tracking branch 'origin/2.2' into 2.2 2024-05-14 17:43:48 +07:00
6772f0cb16 Merge pull request #1498 from GNS3/feature/protocol-handler-service
Create protocol handler service
2024-05-14 17:43:33 +07:00
2730ee6f6e Merge branch 'master' into 2.2 2024-05-14 17:25:07 +07:00
de058e175b Fix tests 2024-05-14 17:15:29 +07:00
e9487c5ada Create protocol handler service 2024-05-14 15:18:59 +07:00
035c846f85 Merge pull request #1495 from GNS3/dependabot/npm_and_yarn/ejs-3.1.10
Bump ejs from 3.1.8 to 3.1.10
2024-05-02 08:54:27 +07:00
80239229aa Bump ejs from 3.1.8 to 3.1.10
Bumps [ejs](https://github.com/mde/ejs) from 3.1.8 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.8...v3.1.10)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-02 01:29:26 +00:00
c12a7acb2c Bump @angular/core from 9.0.0 to 12.2.17
Bumps [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) from 9.0.0 to 12.2.17.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/12.2.17/packages/core)

---
updated-dependencies:
- dependency-name: "@angular/core"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 07:52:40 +00:00
23bf5ad2f3 Merge pull request #1493 from GNS3/dependabot/npm_and_yarn/tar-6.2.1
Bump tar from 6.1.11 to 6.2.1
2024-04-11 14:51:41 +07:00
bed98624cc Bump tar from 6.1.11 to 6.2.1
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.11 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.11...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-10 21:17:40 +00:00
383c26dcef Merge branch 'master' into 2.2 2024-04-03 17:54:45 +07:00
f505c101f7 Merge pull request #1491 from qmwd2006/patch-1
web browser out ouf memory when upload large image file
2024-04-03 17:53:57 +07:00
ca071f79c2 web browser out ouf memory when upload large image file 2024-03-31 07:18:10 +08:00
20be9027fd Bump express from 4.18.2 to 4.19.2
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-28 17:27:08 +00:00
63389812b5 Bump follow-redirects from 1.15.4 to 1.15.6
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-16 23:09:05 +00:00
6873432833 Merge pull request #1488 from GNS3/release/v2.2.46
Release v2.2.46
2024-02-26 17:53:06 +08:00
a7e3c24a27 Development on 2.2.47.dev1 2024-02-26 17:36:11 +08:00
358f50596c Release v2.2.46 2024-02-26 16:29:45 +08:00
b61f5803fd Merge branch 'master' into 2.2 2024-02-26 16:26:56 +08:00
c27f4aee57 Merge pull request #1486 from GNS3/dependabot/npm_and_yarn/ip-1.1.9
Bump ip from 1.1.5 to 1.1.9
2024-02-21 10:53:45 +08:00
5aa00a9f77 Bump ip from 1.1.5 to 1.1.9
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.5 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.5...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 21:27:19 +00:00
91075a60b1 Release v2.2.45 2024-01-12 21:18:37 +11:00
57e590a704 Merge branch 'master' into 2.2 2024-01-12 21:16:07 +11:00
8d3e571aa4 Merge pull request #1485 from GNS3/dependabot/npm_and_yarn/follow-redirects-1.15.4
Bump follow-redirects from 1.14.8 to 1.15.4
2024-01-12 12:21:20 +11:00
af5917b6e4 Bump follow-redirects from 1.14.8 to 1.15.4
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.8 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.8...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 17:38:04 +00:00
21ed977a55 Add ipaddr.js to "allowedCommonJsDependencies" 2023-12-06 22:34:22 +10:00
f861364727 Fix console launch with protocol handler for IPv6 addresses 2023-12-06 15:10:12 +10:00
0ff4d534f4 Development on 2.2.45.dev1 2023-11-06 16:28:27 +10:00
ca408663a5 Merge pull request #1478 from GNS3/release-v2.2.44
Release v2.2.44
2023-11-06 16:27:49 +10:00
c96d66b34a Release v2.2.44 2023-11-06 15:52:05 +10:00
896ca927f3 Add correct protocol when capturing with protocol handler 2023-11-03 14:44:41 +10:00
8253f8da38 Add server protocol to gns3+pcap 2023-11-02 17:36:41 +10:00
cbb1c9ecfc Add project name in gns3+pcap protocol handler URL 2023-10-31 16:45:17 +10:00
1e8b6261dc Merge branch 'master' into 2.2 2023-10-31 16:16:22 +10:00
f3b8a42d89 Merge pull request #1476 from GNS3/console-ipv6
Support IPv6 for external consoles
2023-10-29 15:50:45 +10:00
7812ff38cc Development on 3.0.0.dev10 2023-10-29 15:36:53 +10:00
d725363fe5 Allow edit height and width for rectangles and ellipses 2023-10-24 18:23:55 +10:00
58d42558f7 Merge pull request #1467 from GNS3/dependabot/npm_and_yarn/babel/traverse-7.23.2
Bump @babel/traverse from 7.16.0 to 7.23.2
2023-10-24 18:08:43 +10:00
aecbe32c6c Merge branch 'master' into dependabot/npm_and_yarn/babel/traverse-7.23.2 2023-10-24 17:47:26 +10:00
35193043a2 Merge pull request #1469 from ventaquil/feature/add-qemu-igb-nic
Add Qemu IGB network device
2023-10-24 17:13:59 +10:00
7a229d8e3e Add Qemu IGB network device 2023-10-19 11:23:55 +02:00
ba1180786f Bump @babel/traverse from 7.16.0 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.16.0 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-17 09:34:36 +00:00
535649f0a9 Merge pull request #1458 from GNS3/release-v2.2.42
Release v2.2.42
2023-08-09 22:18:36 +10:00
a4f7db62ba Development on v2.2.43.dev1 2023-08-09 22:14:16 +10:00
662aba4ec8 Release v2.2.42 2023-08-09 20:55:57 +10:00
bfc72c219c Add missing settings to Qemu VM templates. Fixes #1456 2023-08-01 20:13:11 +10:00
997b8df598 Merge pull request #1457 from GNS3/rounded-rect
Support for rounded rectangles
2023-08-01 20:03:55 +10:00
89bff8ac30 Set ry for rectangle 2023-08-01 19:48:08 +10:00
df6248d641 Add corner radius setting to style editor 2023-08-01 19:44:04 +10:00
cefbc3c9be Support for rounded rectangles 2023-08-01 15:54:47 +10:00
de07558349 Merge pull request #1455 from GNS3/release-v2.2.41
Release v2.2.41
2023-07-12 17:10:55 +10:00
b59c528ece Development on v2.2.42.dev1 2023-07-12 16:28:33 +10:00
61334d197d Release v2.2.41 2023-07-12 16:23:12 +10:00
d855e5cb33 Merge branch 'master' into 2.2 2023-07-12 16:09:45 +10:00
b8253d365d Merge pull request #1454 from GNS3/dependabot/npm_and_yarn/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2
2023-07-11 11:09:22 +10:00
05685af5c4 Bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 23:33:31 +00:00
8dbaa11808 Fix issues with default IOU values for memories always checked. Fixes #1453 2023-07-06 21:53:01 +10:00
0d7020af97 Fix issues with Qemu adapters in template preferences. Fixes #1449 2023-07-06 20:39:04 +10:00
58b9083c49 Merge pull request #1442 from GNS3/dependabot/npm_and_yarn/webpack-5.76.0
Bump webpack from 5.62.1 to 5.76.0
2023-06-30 18:30:43 +10:00
43213d0669 Merge pull request #1432 from GNS3/dependabot/npm_and_yarn/http-cache-semantics-4.1.1
Bump http-cache-semantics from 4.1.0 to 4.1.1
2023-06-30 18:21:22 +10:00
924cbe2542 Merge pull request #1431 from GNS3/dependabot/npm_and_yarn/jszip-3.10.1
Bump jszip from 3.7.1 to 3.10.1
2023-06-30 18:21:07 +10:00
d06a3efd2c Merge branch 'master' into dependabot/npm_and_yarn/jszip-3.10.1 2023-06-30 18:08:08 +10:00
e2466ca4ab Merge branch 'master' into dependabot/npm_and_yarn/http-cache-semantics-4.1.1 2023-06-30 18:07:02 +10:00
1da94efe63 Merge branch 'master' into dependabot/npm_and_yarn/webpack-5.76.0 2023-06-30 18:06:16 +10:00
838480509e Support for UEFI boot mode option for Qemu VMs 2023-06-23 11:29:38 +09:30
04c28bd40a Developement on v2.2.41.dev1 2023-06-06 12:23:59 +09:30
328dd37ffe Release v2.2.40 2023-06-06 10:10:29 +09:30
c5a692babf Merge pull request #1445 from GNS3/dependabot/pip/scripts/requests-2.31.0
Bump requests from 2.25.1 to 2.31.0 in /scripts
2023-05-23 08:52:15 +08:00
119afd14d2 Bump requests from 2.25.1 to 2.31.0 in /scripts
Bumps [requests](https://github.com/psf/requests) from 2.25.1 to 2.31.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.25.1...v2.31.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-23 00:51:18 +00:00
37e6921ffb Support HTTP/HTTPS node console. Ref https://github.com/GNS3/gns3-gui/issues/3448 2023-05-22 19:48:23 +08:00
5d48ea046d Development on v2.2.40.dev1 2023-05-08 19:05:57 +08:00
4342d27d07 Release v2.2.39 2023-05-08 18:59:31 +08:00
3394035e2e Bump webpack from 5.62.1 to 5.76.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.62.1 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.62.1...v5.76.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-14 17:07:32 +00:00
49403a5568 Development on v2.2.39.dev1 2023-02-28 15:27:36 +10:00
70e4745657 Merge pull request #1441
Release v2.2.38
2023-02-28 13:21:39 +08:00
543b81c81b Release v2.2.38 2023-02-28 15:17:27 +10:00
02562cd046 Merge pull request #1440 from GNS3/fix/1439
Support extra disk images when importing appliance
2023-02-26 18:42:11 +08:00
252452051a Support extra disk images (hdc/hdd) and cdrom image when importing appliance 2023-02-26 20:15:11 +10:00
a64ff3503e Merge pull request #1433 from GNS3/fix/1336
Fix new template action not using "port_segment_size" and "default_name_format"
2023-02-09 12:20:14 +05:45
318143f5a8 Fix new template action not using "port_segment_size" and "default_name_format" 2023-02-04 13:25:10 +08:00
e029bccf18 Bump http-cache-semantics from 4.1.0 to 4.1.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-03 04:50:10 +00:00
721adacde4 Bump jszip from 3.7.1 to 3.10.1
Bumps [jszip](https://github.com/Stuk/jszip) from 3.7.1 to 3.10.1.
- [Release notes](https://github.com/Stuk/jszip/releases)
- [Changelog](https://github.com/Stuk/jszip/blob/main/CHANGES.md)
- [Commits](https://github.com/Stuk/jszip/compare/v3.7.1...v3.10.1)

---
updated-dependencies:
- dependency-name: jszip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-02 17:12:02 +00:00
4374573c60 Remove cache and scan master branch in codeql.yml 2023-02-01 11:55:08 +08:00
1e7c04f93c Merge pull request #1423 from GNS3/dependabot/pip/scripts/setuptools-65.5.1
Bump setuptools from 54.2.0 to 65.5.1 in /scripts
2023-02-01 08:04:28 +05:45
7e172e30ba Merge pull request #1427 from GNS3/bugfix/1269
Fix console launch on Firefox
2023-01-31 17:27:37 +05:45
4d243f895c Merge pull request #1428 from GNS3/bugfix/1279
Fix error when suspending and configuring a packet filter on a link
2023-01-31 17:26:51 +05:45
aeef3e74ed Fix error when suspending and configuring a packet filter on a link 2023-01-31 19:18:27 +08:00
83d72787f4 Use a hidden iframe to open console on Firefox 2023-01-31 18:56:07 +08:00
829bfe12d7 Merge remote-tracking branch 'origin/master' 2023-01-31 09:45:45 +08:00
f5b5c717b4 Automatically add new issues to GNS3 project 2023-01-31 09:45:37 +08:00
5b7da298d6 Merge pull request #1426 from GNS3/dependabot/npm_and_yarn/ua-parser-js-0.7.33
Bump ua-parser-js from 0.7.31 to 0.7.33
2023-01-27 14:59:33 +05:45
d7742a7c59 Bump ua-parser-js from 0.7.31 to 0.7.33
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.31 to 0.7.33.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.31...0.7.33)

---
updated-dependencies:
- dependency-name: ua-parser-js
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-27 08:44:32 +00:00
e0ce8c0770 Development on v2.2.38.dev1 2023-01-24 15:06:43 +08:00
061dec9d75 Release v2.2.37 2023-01-24 15:00:34 +08:00
684a160d99 Merge remote-tracking branch 'origin/master' 2023-01-04 19:03:48 +08:00
21a12c151b Development on v2.2.37dev 2023-01-04 19:03:17 +08:00
17be201862 Merge pull request #1424 from GNS3/dependabot/npm_and_yarn/json5-1.0.2
Bump json5 from 1.0.1 to 1.0.2
2023-01-04 16:43:01 +05:45
57385b84f7 Bump json5 from 1.0.1 to 1.0.2
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-04 10:57:08 +00:00
8e4f860b43 Release v2.2.36 2023-01-04 18:56:35 +08:00
2c015e695d Add TPM support for Qemu VMs 2023-01-04 18:52:46 +08:00
9fe2b3646b Bump setuptools from 54.2.0 to 65.5.1 in /scripts
Bumps [setuptools](https://github.com/pypa/setuptools) from 54.2.0 to 65.5.1.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/CHANGES.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v54.2.0...v65.5.1)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-27 15:56:29 +00:00
313966548f Scan master-3.0 branch with Mend 2022-12-27 13:21:53 +08:00
69a7cf44c5 Merge pull request #1420 from GNS3/dependabot/npm_and_yarn/snyk-1.1064.0
Bump snyk from 1.996.0 to 1.1064.0
2022-12-20 20:01:32 +05:45
faec4b07be Bump snyk from 1.996.0 to 1.1064.0
Bumps [snyk](https://github.com/snyk/snyk) from 1.996.0 to 1.1064.0.
- [Release notes](https://github.com/snyk/snyk/releases)
- [Commits](https://github.com/snyk/snyk/compare/v1.996.0...v1.1064.0)

---
updated-dependencies:
- dependency-name: snyk
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-20 13:40:30 +00:00
908c721094 Create SECURITY.md 2022-12-20 21:34:44 +08:00
e3c4188171 Create codeql.yml 2022-12-20 21:32:46 +08:00
f48471cdd4 Merge pull request #1418 from GNS3/dependabot/npm_and_yarn/express-4.18.2
Bump express from 4.17.1 to 4.18.2
2022-12-07 13:17:33 +05:45
50fb05aa8e Bump express from 4.17.1 to 4.18.2
Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.18.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.17.1...4.18.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-07 07:18:48 +00:00
63728091c1 Merge pull request #1417 from GNS3/dependabot/npm_and_yarn/qs-6.5.3
Bump qs from 6.5.2 to 6.5.3
2022-12-07 13:03:17 +05:45
03a417d78c Bump qs from 6.5.2 to 6.5.3
Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/ljharb/qs/releases)
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3)

---
updated-dependencies:
- dependency-name: qs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-07 03:40:35 +00:00
c7a7a357c6 Merge pull request #1409 from GNS3/dependabot/npm_and_yarn/socket.io-parser-4.0.5
Bump socket.io-parser from 4.0.4 to 4.0.5
2022-12-03 21:16:35 +08:00
7d95267283 Merge pull request #1416 from GNS3/dependabot/npm_and_yarn/decode-uri-component-0.2.2
Bump decode-uri-component from 0.2.0 to 0.2.2
2022-12-03 21:16:13 +08:00
8a7309bde1 Bump decode-uri-component from 0.2.0 to 0.2.2
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-03 00:10:34 +00:00
afccf4955b Merge pull request #1412 from GNS3/dependabot/npm_and_yarn/loader-utils-1.4.2
Bump loader-utils from 1.4.1 to 1.4.2
2022-11-16 18:36:13 +08:00
ad57a5f3f7 Bump loader-utils from 1.4.1 to 1.4.2
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.2/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-16 07:53:15 +00:00
7407ddafd3 Bump socket.io-parser from 4.0.4 to 4.0.5
Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.0.4 to 4.0.5.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-parser/compare/4.0.4...4.0.5)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-09 22:03:22 +00:00
168de3aecb Development on v2.2.36dev 2022-11-08 19:15:15 +08:00
13f80cdaad Merge remote-tracking branch 'origin/master' 2022-11-08 19:13:48 +08:00
28f32de0b2 Release v2.2.35 2022-11-08 19:13:39 +08:00
a6a4fb401d Merge pull request #1406 from GNS3/dependabot/npm_and_yarn/loader-utils-1.4.1
Bump loader-utils from 1.4.0 to 1.4.1
2022-11-08 18:24:57 +08:00
f62366440c Bump loader-utils from 1.4.0 to 1.4.1
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.1/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 08:31:46 +00:00
135ecbdc33 Merge pull request #1403 from GNS3/dependabot/npm_and_yarn/node-fetch-3.2.10
Bump node-fetch from 3.1.1 to 3.2.10
2022-10-18 22:02:17 +08:00
9ebbbb197b Bump node-fetch from 3.1.1 to 3.2.10
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 3.1.1 to 3.2.10.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v3.1.1...v3.2.10)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-07 08:23:13 +00:00
f90c074191 Merge pull request #1402 from GNS3/dependabot/npm_and_yarn/snyk-1.996.0
Bump snyk from 1.780.0 to 1.996.0
2022-10-07 10:22:46 +02:00
2d49ca30fa Bump snyk from 1.780.0 to 1.996.0
Bumps [snyk](https://github.com/snyk/snyk) from 1.780.0 to 1.996.0.
- [Release notes](https://github.com/snyk/snyk/releases)
- [Commits](https://github.com/snyk/snyk/compare/v1.780.0...v1.996.0)

---
updated-dependencies:
- dependency-name: snyk
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-06 18:40:49 +00:00
83f7d36e2d Add more video resolutions to Docker containers using VNC in WebUI. Fixes #1375 2022-08-07 23:59:44 +02:00
174053f297 Development on v2.2.34dev 2022-06-20 20:40:33 +02:00
0e4e124c14 Release v2.2.33 2022-06-20 20:24:06 +02:00
c928ab0342 Merge pull request #1332 from GNS3/bugfix/1329
Fix spice+agent and none console types not supported
2022-06-20 18:38:17 +02:00
9efd99dccb Fix spice+agent and none console types not supported. 2022-06-18 18:53:36 +02:00
7e43dc77cb Merge pull request #1322 from GNS3/dependabot/npm_and_yarn/async-2.6.4
Bump async from 2.6.3 to 2.6.4
2022-06-10 00:33:25 +08:00
d7752a4d7b Bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-01 07:52:58 +00:00
40df0fe1ee Merge pull request #1321 from GNS3/dependabot/npm_and_yarn/ejs-3.1.8
Bump ejs from 3.1.6 to 3.1.8
2022-06-01 14:52:11 +07:00
d5bd84234d Bump ejs from 3.1.6 to 3.1.8
Bumps [ejs](https://github.com/mde/ejs) from 3.1.6 to 3.1.8.
- [Release notes](https://github.com/mde/ejs/releases)
- [Changelog](https://github.com/mde/ejs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mde/ejs/compare/v3.1.6...v3.1.8)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-01 07:13:32 +00:00
a9a7ecf3e7 Merge pull request #1320 from GNS3/dependabot/npm_and_yarn/eventsource-1.1.1
Bump eventsource from 1.1.0 to 1.1.1
2022-06-01 14:12:30 +07:00
5dc5a953e6 Bump eventsource from 1.1.0 to 1.1.1
Bumps [eventsource](https://github.com/EventSource/eventsource) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/EventSource/eventsource/releases)
- [Changelog](https://github.com/EventSource/eventsource/blob/master/HISTORY.md)
- [Commits](https://github.com/EventSource/eventsource/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: eventsource
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-01 01:20:11 +00:00
32c78450a2 Development on 2.2.33dev 2022-04-27 17:53:00 +07:00
82feb9aa92 Release web UI 2.2.32 2022-04-27 17:48:47 +07:00
a08a7e1476 Fix for issue in docker configurator 2022-04-27 10:35:54 +02:00
b5e4972bdb Fix for #1303 2022-04-26 11:24:00 +02:00
dc5c0d3d94 Merge pull request #1302 from GNS3/bugfix/1298
Fix capture file name
2022-04-26 14:52:49 +07:00
04936cfc8d Fix generated capture file is not valid 2022-04-21 13:53:27 +07:00
8728056b8d Merge pull request #1296 from GNS3/dependabot/npm_and_yarn/plist-3.0.5
Bump plist from 3.0.4 to 3.0.5
2022-04-13 16:15:02 +07:00
138d1f8552 Merge pull request #1283 from GNS3/dependabot/npm_and_yarn/electron-13.6.6
Bump electron from 13.6.1 to 13.6.6
2022-04-13 16:14:26 +07:00
dc31d51844 Bump plist from 3.0.4 to 3.0.5
Bumps [plist](https://github.com/TooTallNate/node-plist) from 3.0.4 to 3.0.5.
- [Release notes](https://github.com/TooTallNate/node-plist/releases)
- [Changelog](https://github.com/TooTallNate/plist.js/blob/master/History.md)
- [Commits](https://github.com/TooTallNate/node-plist/commits)

---
updated-dependencies:
- dependency-name: plist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-12 10:07:20 +00:00
def33a353d Bump minimist from 1.2.5 to 1.2.6 (#1294)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-12 12:06:10 +02:00
ed3db2ea4d Bump electron from 13.6.1 to 13.6.6
Bumps [electron](https://github.com/electron/electron) from 13.6.1 to 13.6.6.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v13.6.1...v13.6.6)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-25 18:37:29 +00:00
7ad6de2256 Merge pull request #1261 from GNS3/dependabot/npm_and_yarn/url-parse-1.5.10
Bump url-parse from 1.5.3 to 1.5.10
2022-03-13 20:20:17 +10:00
6dcc5cdc2e Bump url-parse from 1.5.3 to 1.5.10
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.10.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.10)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 01:08:02 +00:00
089e66a02b Bump karma from 6.3.15 to 6.3.16 (#1263)
Bumps [karma](https://github.com/karma-runner/karma) from 6.3.15 to 6.3.16.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.3.15...v6.3.16)

---
updated-dependencies:
- dependency-name: karma
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-07 01:16:14 +01:00
da848d42af Release web UI 2.2.30 2022-02-16 01:02:04 +01:00
6b08fb8d9a Bump follow-redirects from 1.14.7 to 1.14.8 (#1254)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-16 00:49:50 +01:00
8874e7efbc Refreshing UI (#1253)
* Update font-fixer.ts

* Update interface-status.ts

* Updating project map UI

* Updating templates menu

* Fixing tests

* Update karma.conf.js
2022-02-08 23:48:45 +01:00
2b834768c6 Update karma.conf.js 2022-02-08 23:47:22 +01:00
eabdda0e74 Update dependency karma to v6.3.14 (#1251)
Co-authored-by: whitesource-for-github-com[bot] <whitesource-for-github-com[bot]@users.noreply.github.com>
2022-02-08 14:05:07 +01:00
9e3f667767 Update dependency karma to v6.3.9 (#1247)
Co-authored-by: whitesource-for-github-com[bot] <whitesource-for-github-com[bot]@users.noreply.github.com>
2022-01-31 14:07:59 +01:00
8898141bc1 Bump nanoid from 3.1.30 to 3.2.0 (#1236)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.30 to 3.2.0.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.30...3.2.0)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-31 10:42:53 +01:00
c8753ed45c Bump node-fetch from 3.0.0 to 3.1.1 (#1237)
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 3.0.0 to 3.1.1.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v3.0.0...v3.1.1)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-31 10:22:43 +01:00
d496d8dc64 Bump log4js from 6.3.0 to 6.4.0 (#1235)
Bumps [log4js](https://github.com/log4js-node/log4js-node) from 6.3.0 to 6.4.0.
- [Release notes](https://github.com/log4js-node/log4js-node/releases)
- [Changelog](https://github.com/log4js-node/log4js-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/log4js-node/log4js-node/compare/v6.3.0...v6.4.0)

---
updated-dependencies:
- dependency-name: log4js
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-31 00:21:55 +01:00
a8f9b6948d Bump follow-redirects from 1.14.5 to 1.14.7 (#1233)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.5 to 1.14.7.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.5...v1.14.7)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-31 00:01:23 +01:00
ccd3ff61f1 Release web UI 2.2.29 2022-01-07 20:04:51 +01:00
abf9d8b387 Release web UI 2.2.28 2021-12-13 23:40:20 +01:00
f8cc654539 Update project-map.component.ts 2021-12-13 23:34:03 +01:00
56554f6d0c Update project-map.component.ts 2021-12-13 23:22:15 +01:00
7be137dc1c Merge pull request #1225 from GNS3/Remember-topology/servers-window-position
Update topology-summary.component.ts
2021-12-12 12:46:39 -08:00
1b45a2284d Merge pull request #1224 from GNS3/Mouse-pointer-#1219
Mouse pointer #1219
2021-12-12 12:46:11 -08:00
6b4f5186d0 Update topology-summary.component.ts 2021-12-09 02:27:19 +01:00
c3f2ebad0c Mouse pointer #1219 2021-12-08 19:18:38 +01:00
249f63a97a Update new-template-dialog.component.html 2021-12-07 20:03:36 +01:00
cc6b8cd28c Update configurator-docker.component.ts 2021-12-07 15:51:26 +01:00
96928d86f8 Updating snyk version 2021-12-02 00:51:24 +01:00
a213a7aca1 Angular version set to 12.2.12 2021-11-10 00:12:32 +01:00
fc1d17b921 Reverting updates 2021-11-09 23:28:52 +01:00
0ddf4f6e95 Release web UI 2.2.27 2021-11-08 18:09:29 +01:00
8d466d655e Updating dependencies 2021-11-08 18:03:17 +01:00
2c7dd5f179 Removing sentry/cli 2021-11-08 17:48:58 +01:00
64999f2b72 Removing electron builder 2021-11-08 17:36:51 +01:00
253c65b8c1 Fix for builds on github 2021-11-08 16:49:45 +01:00
74c1a82524 Update .gitignore 2021-11-08 13:41:11 +01:00
4e42bd7a54 Merge pull request #1199 from potats0/master
add progress bar when uploading qemu disks
2021-11-07 13:54:30 -08:00
891e65b094 Update yarn.lock 2021-11-07 18:46:57 +01:00
c808477914 Fix for error with component factory after migration to angular v13 2021-11-07 18:32:34 +01:00
8503a17187 Updating angular material 2021-11-07 18:24:48 +01:00
8afea664ff updating packages 2021-11-07 17:28:04 +01:00
538ae8b7fb Update yarn.lock 2021-11-07 16:58:08 +01:00
370694f3b0 Update package.json 2021-11-07 16:55:02 +01:00
5175b3beac Updating packages 2021-11-07 16:48:17 +01:00
2df1956dbc Updating packages 2021-11-02 22:39:08 +01:00
56384fbcc0 fix progress bar incorrect when upload file twice 2021-10-14 12:22:15 +08:00
15faca6d89 show upload file progress when uploading qemu template. 2021-10-14 10:56:17 +08:00
4142144d4d Update package.json 2021-10-06 18:08:56 +02:00
e2e87db039 Release 2.2.26 2021-10-06 17:09:46 +02:00
c868f08a25 Update yarn.lock 2021-10-06 17:03:49 +02:00
9aedd410bb Update yarn.lock 2021-10-06 14:02:26 +02:00
5fb76d7d11 Update yarn.lock 2021-10-06 13:52:57 +02:00
a7c343aa7c Merge pull request #1194 from GNS3/dependabot/npm_and_yarn/nth-check-2.0.1
Bump nth-check from 2.0.0 to 2.0.1
2021-10-06 04:47:47 -07:00
cfe8c4760b Updating dependencies 2021-10-06 13:34:19 +02:00
7cbcc84cc1 Update template.component.ts 2021-10-06 12:57:40 +02:00
357e478fb8 Fix for theming 2021-10-06 12:44:23 +02:00
063d8c9dc7 Bump nth-check from 2.0.0 to 2.0.1
Bumps [nth-check](https://github.com/fb55/nth-check) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/fb55/nth-check/releases)
- [Commits](https://github.com/fb55/nth-check/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: nth-check
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-22 12:57:14 +00:00
63ecacb6b6 Update package.json 2021-09-14 12:01:56 +02:00
6cecacf611 Release 2.2.25 2021-09-14 10:22:36 +02:00
c389404e58 Updating dependencies 2021-09-14 10:06:47 +02:00
b1aba60410 Option to access system status from servers page 2021-09-14 01:01:46 +02:00
4cd9f77732 Release 2.2.24 2021-08-25 11:52:56 +02:00
1619c3ec05 Fix for https://github.com/GNS3/gns3-web-ui/issues/1184 2021-08-24 17:07:47 +02:00
b33a01e225 Update yarn.lock 2021-08-24 15:12:51 +02:00
353740376e Updating angular-devkit/build-angular 2021-08-24 14:53:51 +02:00
9fe899e4df Removing vulnerabilities 2021-08-24 14:39:02 +02:00
dd1f16c53d Merge pull request #1187 from GNS3/dependabot/npm_and_yarn/url-parse-1.5.3
Bump url-parse from 1.5.1 to 1.5.3
2021-08-24 05:00:53 -07:00
3fa52d3c9c Updating dependencies 2021-08-24 13:55:15 +02:00
bc5dd0271f Bump url-parse from 1.5.1 to 1.5.3
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.3)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-24 11:04:39 +00:00
81ca3e2af2 Merge pull request #1183 from GNS3/dependabot/npm_and_yarn/tar-6.1.6
Bump tar from 6.1.0 to 6.1.6
2021-08-24 04:03:51 -07:00
d7a0d2f69a Bump tar from 6.1.0 to 6.1.6
Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.6.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.6)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-04 09:11:45 +00:00
8f0bbafa72 Updating dependencies 2021-08-04 11:10:41 +02:00
81 changed files with 3907 additions and 4666 deletions

View File

@ -0,0 +1,16 @@
name: Add new issues to GNS3 project
on:
issues:
types:
- opened
jobs:
add-to-project:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v0.4.0
with:
project-url: https://github.com/orgs/GNS3/projects/3
github-token: ${{ secrets.ADD_NEW_ISSUES_TO_PROJECT }}

76
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,76 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master", "master-3.0" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master", "master-3.0" ]
schedule:
- cron: '38 18 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output # compiled output
/.angular
/dist /dist
/tmp /tmp
/out-tsc /out-tsc

View File

@ -2,7 +2,8 @@
"scanSettings": { "scanSettings": {
"configMode": "AUTO", "configMode": "AUTO",
"configExternalURL": "", "configExternalURL": "",
"projectToken" : "" "projectToken" : "",
"baseBranches": ["master", "master-3.0"]
}, },
"checkRunSettings": { "checkRunSettings": {
"vulnerableCheckRunConclusionLevel": "failure" "vulnerableCheckRunConclusionLevel": "failure"
@ -10,4 +11,4 @@
"issueSettings": { "issueSettings": {
"minSeverityLevel": "LOW" "minSeverityLevel": "LOW"
} }
} }

5
SECURITY.md Normal file
View File

@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
Please use GitHub's report a vulnerability feature. More information can be found in https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability

View File

@ -26,8 +26,9 @@
"rxjs/add/operator/map", "rxjs/add/operator/map",
"rxjs-compat/add/operator/map", "rxjs-compat/add/operator/map",
"classnames", "classnames",
"stylenames" "stylenames",
], "ipaddr.js"
],
"outputPath": "dist", "outputPath": "dist",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
@ -47,10 +48,11 @@
"scripts": [], "scripts": [],
"vendorChunk": true, "vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,
"buildOptimizer": false, "buildOptimizer": true,
"sourceMap": true, "sourceMap": true,
"optimization": false, "optimization": false,
"namedChunks": true "namedChunks": true,
"aot": true
}, },
"configurations": { "configurations": {
"production": { "production": {
@ -176,6 +178,7 @@
"src/styles.scss", "src/styles.scss",
"src/theme.scss" "src/theme.scss"
], ],
"sourceMap": false,
"assets": [ "assets": [
"src/assets", "src/assets",
"src/favicon.ico" "src/favicon.ico"
@ -241,4 +244,4 @@
"cli": { "cli": {
"analytics": false "analytics": false
} }
} }

View File

@ -25,7 +25,7 @@ module.exports = function (config) {
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
browsers: ['ChromeHeadless'], browsers: ['Chrome'],
singleRun: true singleRun: true
}); });
}; };

View File

@ -1,6 +1,6 @@
{ {
"name": "gns3-web-ui", "name": "gns3-web-ui",
"version": "2.2.23", "version": "2.2.49",
"author": { "author": {
"name": "GNS3 Technology Inc.", "name": "GNS3 Technology Inc.",
"email": "developers@gns3.com" "email": "developers@gns3.com"
@ -36,34 +36,37 @@
"generate-licenses-file": "yarn license-checker --production --csv --out licenses.csv", "generate-licenses-file": "yarn license-checker --production --csv --out licenses.csv",
"prebuildforelectron": "node set-variables-in-env.js --set src/environments/environment.electron.prod.ts", "prebuildforelectron": "node set-variables-in-env.js --set src/environments/environment.electron.prod.ts",
"postbuildforelectron": "node set-variables-in-env.js --unset src/environments/environment.electron.prod.ts", "postbuildforelectron": "node set-variables-in-env.js --unset src/environments/environment.electron.prod.ts",
"postinstall": "ngcc --properties es5 browser module main --first-only --create-ivy-entry-points && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
"snyk-protect": "snyk protect", "snyk-protect": "snyk protect",
"prepare": "yarn run snyk-protect" "prepare": "yarn run snyk-protect"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^12.1.4", "@angular/animations": "^12.2.12",
"@angular/cdk": "^12.1.4", "@angular/cdk": "^12.2.12",
"@angular/common": "^12.1.4", "@angular/common": "^12.2.12",
"@angular/compiler": "^12.1.4", "@angular/compiler": "^12.2.12",
"@angular/core": "^12.1.4", "@angular/core": "^12.2.12",
"@angular/forms": "^12.1.4", "@angular/forms": "^12.2.12",
"@angular/material": "^12.1.4", "@angular/material": "^12.2.12",
"@angular/platform-browser": "^12.1.4", "@angular/platform-browser": "^12.2.12",
"@angular/platform-browser-dynamic": "^12.1.4", "@angular/platform-browser-dynamic": "^12.2.12",
"@angular/router": "^12.1.4", "@angular/router": "^12.2.12",
"@sentry/browser": "^6.10.0", "@sentry/browser": "^6.14.1",
"@types/jest": "^26.0.24", "@types/jest": "^27.0.2",
"@types/mocha": "^9.0.0", "@types/mocha": "^9.0.0",
"@types/react": "^17.0.15", "@types/react": "^17.0.34",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.11",
"angular-draggable-droppable": "^4.6.0", "angular-draggable-droppable": "^5.0.0",
"angular-resizable-element": "^3.4.0", "angular-resizable-element": "^3.4.0",
"bootstrap": "^5.0.2", "bootstrap": "^5.1.3",
"command-exists": "^1.2.9", "command-exists": "^1.2.9",
"core-js": "^3.16.0", "core-js": "^3.19.1",
"css-tree": "^1.1.3",
"d3-ng2-service": "^2.2.0", "d3-ng2-service": "^2.2.0",
"eev": "^0.1.5", "eev": "^0.1.5",
"ini": "^2.0.0", "ini": "^2.0.0",
"ipaddr.js": "^2.1.0",
"material-design-icons": "^3.0.1", "material-design-icons": "^3.0.1",
"mousetrap": "^1.6.5", "mousetrap": "^1.6.5",
"ng-circle-progress": "^1.6.0", "ng-circle-progress": "^1.6.0",
@ -71,42 +74,42 @@
"ngx-childprocess": "^0.0.6", "ngx-childprocess": "^0.0.6",
"ngx-device-detector": "^2.1.1", "ngx-device-detector": "^2.1.1",
"ngx-electron": "^2.2.0", "ngx-electron": "^2.2.0",
"node-fetch": "^2.6.1", "node-fetch": "^3.2.10",
"notosans-fontface": "1.2.2", "notosans-fontface": "1.2.2",
"prettier-plugin-organize-imports": "^2.3.3", "prettier-plugin-organize-imports": "^2.3.4",
"rxjs": "^6.5.3", "rxjs": "^6.6.7",
"rxjs-compat": "^6.5.3", "rxjs-compat": "^6.6.7",
"save-svg-as-png": "^1.4.17", "save-svg-as-png": "^1.4.17",
"snyk": "^1.671.0", "snyk": "^1.1064.0",
"spark-md5": "^3.0.1", "spark-md5": "^3.0.2",
"svg-crowbar": "^0.6.5", "svg-crowbar": "^0.7.0",
"tree-kill": "^1.2.2", "tree-kill": "^1.2.2",
"tslib": "^2.3.0", "tslib": "^2.3.1",
"typeface-roboto": "^1.1.13", "typeface-roboto": "^1.1.13",
"xterm": "^4.13.0", "xterm": "^4.15.0",
"xterm-addon-attach": "^0.6.0", "xterm-addon-attach": "^0.6.0",
"xterm-addon-fit": "^0.5.0", "xterm-addon-fit": "^0.5.0",
"yargs": "^17.0.1", "yargs": "^17.2.1",
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^12.1.4", "@angular-devkit/build-angular": "^12.2.12",
"@angular/cli": "^12.1.4", "@angular/cli": "^12.2.12",
"@angular/compiler-cli": "^12.1.4", "@angular/compiler-cli": "^12.2.12",
"@angular/language-service": "^12.1.4", "@angular/language-service": "^12.2.12",
"@sentry/cli": "^1.67.2", "@sentry/cli": "^1.71.0",
"@sentry/electron": "^2.5.1", "@sentry/electron": "^2.5.4",
"@types/jasmine": "^3.8.2", "@types/jasmine": "^3.10.2",
"@types/jasminewd2": "^2.0.10", "@types/jasminewd2": "^2.0.10",
"@types/node": "16.4.7", "@types/node": "16.11.6",
"codelyzer": "^6.0.2", "codelyzer": "^6.0.2",
"electron": "^13.1.7", "electron": "^13.6.6",
"electron-builder": "22.11.7", "electron-builder": "^22.9.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"jasmine-core": "~3.8.0", "jasmine-core": "~3.10.1",
"jasmine-spec-reporter": "~7.0.0", "jasmine-spec-reporter": "~7.0.0",
"jquery": "^3.6.0", "jquery": "^3.6.0",
"karma": "^6.3.4", "karma": "^6.3.16",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
"karma-cli": "^2.0.0", "karma-cli": "^2.0.0",
"karma-coverage-istanbul-reporter": "~3.0.3", "karma-coverage-istanbul-reporter": "~3.0.3",
@ -114,16 +117,16 @@
"karma-jasmine-html-reporter": "^1.7.0", "karma-jasmine-html-reporter": "^1.7.0",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"prettier": "^2.3.2", "prettier": "^2.4.1",
"protractor": "^7.0.0", "protractor": "^7.0.0",
"replace": "^1.2.1", "replace": "^1.2.1",
"rxjs-tslint": "^0.1.8", "rxjs-tslint": "^0.1.8",
"ts-mockito": "^2.6.1", "ts-mockito": "^2.6.1",
"ts-node": "~10.1.0", "ts-node": "~10.4.0",
"tslint": "^6.1.3", "tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0", "tslint-config-prettier": "^1.18.0",
"typescript": "4.3.5", "typescript": "4.2.3",
"webpack": "5.47.1", "webpack": "5.76.0",
"yarn-upgrade-all": "^0.5.4" "yarn-upgrade-all": "^0.5.4"
}, },
"greenkeeper": { "greenkeeper": {
@ -132,4 +135,4 @@
] ]
}, },
"snyk": true "snyk": true
} }

View File

@ -1,6 +1,6 @@
setuptools==54.2.0 setuptools==71.1.0
cx_Freeze==5.1.1 cx_Freeze==5.1.1
requests==2.25.1 requests==2.32.3
packaging==20.9 packaging==20.9
appdirs==1.4.4 appdirs==1.4.4
psutil==5.8.0 psutil==5.8.0

View File

@ -1,6 +1,10 @@
GNS3 WebUI is web implementation of user interface for GNS3 software. GNS3 WebUI is web implementation of user interface for GNS3 software.
Current version: 2.2.22 Current version: 2.2.32
Bug Fixes & enhancements
- Fixed generated capture file is not valid
- Fixed Docker additional directories
Current version: 2020.4.0-beta.1 Current version: 2020.4.0-beta.1

View File

@ -1,4 +1,4 @@
<div [ngClass]="{ dark: darkThemeEnabled, light: !darkThemeEnabled }"> <div [ngClass]="{ dark: darkThemeEnabled, light: !darkThemeEnabled }">
<router-outlet></router-outlet> <router-outlet></router-outlet>
<app-adbutler></app-adbutler> <!-- <app-adbutler></app-adbutler> -->
</div> </div>

View File

@ -215,6 +215,7 @@ import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.
import { MATERIAL_IMPORTS } from './material.imports'; import { MATERIAL_IMPORTS } from './material.imports';
import { ServerResolve } from './resolvers/server-resolve'; import { ServerResolve } from './resolvers/server-resolve';
import { ApplianceService } from './services/appliances.service'; import { ApplianceService } from './services/appliances.service';
import { ProtocolHandlerService } from './services/protocol-handler.service';
import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service'; import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service';
import { BuiltInTemplatesService } from './services/built-in-templates.service'; import { BuiltInTemplatesService } from './services/built-in-templates.service';
import { ComputeService } from './services/compute.service'; import { ComputeService } from './services/compute.service';
@ -538,6 +539,7 @@ import { RotationValidator } from './validators/rotation-validator';
ComputeService, ComputeService,
TracengService, TracengService,
PacketCaptureService, PacketCaptureService,
ProtocolHandlerService,
NotificationService, NotificationService,
Gns3vmService, Gns3vmService,
ThemeService, ThemeService,

View File

@ -7,4 +7,6 @@
[attr.stroke-dasharray]="stroke_dasharray" [attr.stroke-dasharray]="stroke_dasharray"
[attr.width]="rect.width" [attr.width]="rect.width"
[attr.height]="rect.height" [attr.height]="rect.height"
[attr.rx]="rect.rx"
[attr.ry]="rect.ry"
/> />

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 322 B

View File

@ -87,6 +87,7 @@ export class TextEditorComponent implements OnInit, OnDestroy {
`scale(${this.mapScaleService.getScale()})` `scale(${this.mapScaleService.getScale()})`
); );
this.temporaryTextElement.nativeElement.focus(); this.temporaryTextElement.nativeElement.focus();
document.documentElement.style.cursor = "default";
let textListener = () => { let textListener = () => {
this.drawingsEventSource.textAdded.emit( this.drawingsEventSource.textAdded.emit(

View File

@ -14,7 +14,7 @@ export class MapDrawingToSvgConverter implements Converter<MapDrawing, string> {
let elem = ``; let elem = ``;
if (mapDrawing.element instanceof RectElement) { if (mapDrawing.element instanceof RectElement) {
elem = `<rect fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`; elem = `<rect fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" rx=\"${mapDrawing.element.rx}\" ry=\"${mapDrawing.element.ry}\" />`;
} else if (mapDrawing.element instanceof EllipseElement) { } else if (mapDrawing.element instanceof EllipseElement) {
elem = `<ellipse fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" cx=\"${mapDrawing.element.cx}\" cy=\"${mapDrawing.element.cy}\" rx=\"${mapDrawing.element.rx}\" ry=\"${mapDrawing.element.ry}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`; elem = `<ellipse fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" cx=\"${mapDrawing.element.cx}\" cy=\"${mapDrawing.element.cy}\" rx=\"${mapDrawing.element.rx}\" ry=\"${mapDrawing.element.ry}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`;
} else if (mapDrawing.element instanceof LineElement) { } else if (mapDrawing.element instanceof LineElement) {

View File

@ -13,6 +13,8 @@ export class RectangleElementFactory implements DrawingElementFactory {
rectElement.stroke_width = 2; rectElement.stroke_width = 2;
rectElement.width = 200; rectElement.width = 200;
rectElement.height = 100; rectElement.height = 100;
rectElement.rx = 0;
rectElement.ry = 0;
return rectElement; return rectElement;
} }
} }

View File

@ -16,8 +16,8 @@ describe('FontFixer', () => {
}; };
expect(fixer.fix(font)).toEqual({ expect(fixer.fix(font)).toEqual({
font_family: 'Noto Sans', font_family: 'Arial',
font_size: 11, font_size: 12,
font_weight: 'bold', font_weight: 'bold',
}); });
}); });
@ -39,12 +39,12 @@ describe('FontFixer', () => {
it('should fix TypeWriter font and 10px size in styles', () => { it('should fix TypeWriter font and 10px size in styles', () => {
const styles = 'font-family: TypeWriter; font-size: 10px; font-weight: bold'; const styles = 'font-family: TypeWriter; font-size: 10px; font-weight: bold';
expect(fixer.fixStyles(styles)).toEqual('font-family:Noto Sans;font-size:11px;font-weight:bold'); expect(fixer.fixStyles(styles)).toEqual('font-family:Arial;font-size:12px;font-weight:bold');
}); });
it('should fix TypeWriter font and 10px size in styles with quotes', () => { it('should fix TypeWriter font and 10px size in styles with quotes', () => {
const styles = 'font-family: "TypeWriter"; font-size: 10px; font-weight: bold'; const styles = 'font-family: "TypeWriter"; font-size: 10px; font-weight: bold';
expect(fixer.fixStyles(styles)).toEqual('font-family:Noto Sans;font-size:11px;font-weight:bold'); expect(fixer.fixStyles(styles)).toEqual('font-family:Arial;font-size:12px;font-weight:bold');
}); });
}); });

View File

@ -9,8 +9,8 @@ import { Font } from '../models/font';
export class FontFixer { export class FontFixer {
static DEFAULT_FONT = 'TypeWriter'; static DEFAULT_FONT = 'TypeWriter';
static DEFAULT_SIZE = 10; static DEFAULT_SIZE = 10;
static REPLACE_BY_FONT = 'Noto Sans'; static REPLACE_BY_FONT = 'Arial';
static REPLACE_BY_SIZE = 11; static REPLACE_BY_SIZE = 12;
public fix(font: Font): Font { public fix(font: Font): Font {
if (font.font_family === FontFixer.DEFAULT_FONT && font.font_size === FontFixer.DEFAULT_SIZE) { if (font.font_family === FontFixer.DEFAULT_FONT && font.font_size === FontFixer.DEFAULT_SIZE) {

View File

@ -17,6 +17,8 @@ describe('RectConverter', () => {
element.setAttribute('width', '100px'); element.setAttribute('width', '100px');
element.setAttribute('height', '200px'); element.setAttribute('height', '200px');
element.setAttribute('rx', '0');
element.setAttribute('ry', '0');
const drawing = rectConverter.convert(element); const drawing = rectConverter.convert(element);
expect(drawing.fill).toEqual('#ffffff'); expect(drawing.fill).toEqual('#ffffff');
@ -25,6 +27,8 @@ describe('RectConverter', () => {
expect(drawing.stroke_dasharray).toEqual('5,25,25'); expect(drawing.stroke_dasharray).toEqual('5,25,25');
expect(drawing.width).toEqual(100); expect(drawing.width).toEqual(100);
expect(drawing.height).toEqual(200); expect(drawing.height).toEqual(200);
expect(drawing.rx).toEqual(0);
expect(drawing.ry).toEqual(0);
}); });
it('should parse with no attributes', () => { it('should parse with no attributes', () => {
@ -37,5 +41,7 @@ describe('RectConverter', () => {
expect(drawing.stroke_dasharray).toBeUndefined(); expect(drawing.stroke_dasharray).toBeUndefined();
expect(drawing.width).toBeUndefined(); expect(drawing.width).toBeUndefined();
expect(drawing.height).toBeUndefined(); expect(drawing.height).toBeUndefined();
expect(drawing.rx).toBeUndefined();
expect(drawing.ry).toBeUndefined();
}); });
}); });

View File

@ -40,6 +40,16 @@ export class RectConverter implements SvgConverter {
drawing.height = parseInt(height.value, 10); drawing.height = parseInt(height.value, 10);
} }
const rx = element.attributes.getNamedItem('rx');
if (rx) {
drawing.rx = parseInt(rx.value, 0);
}
const ry = element.attributes.getNamedItem('ry');
if (ry) {
drawing.ry = parseInt(ry.value, 0);
}
return drawing; return drawing;
} }
} }

View File

@ -8,4 +8,6 @@ export class RectElement implements DrawingElement {
stroke: string; stroke: string;
stroke_width: number; stroke_width: number;
stroke_dasharray: string; stroke_dasharray: string;
rx: number;
ry: number;
} }

View File

@ -14,6 +14,7 @@ export class Properties {
headless: boolean; headless: boolean;
linked_clone: boolean; linked_clone: boolean;
on_close: string; on_close: string;
aux: number;
ram: number; ram: number;
nvram: number; nvram: number;
usage: string; usage: string;
@ -53,7 +54,10 @@ export class Properties {
qemu_path: string; qemu_path: string;
environment: string; environment: string;
extra_hosts: string; extra_hosts: string;
extra_volumes: string[];
replicate_network_connection_state: boolean; replicate_network_connection_state: boolean;
tpm: boolean;
uefi: boolean;
} }
export class Node { export class Node {

View File

@ -28,6 +28,8 @@ describe('RectDrawingWidget', () => {
rect.stroke_dasharray = '5,25,25'; rect.stroke_dasharray = '5,25,25';
rect.width = 100; rect.width = 100;
rect.height = 200; rect.height = 200;
rect.rx = 0;
rect.ry = 0;
drawing.element = rect; drawing.element = rect;
const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
@ -46,5 +48,7 @@ describe('RectDrawingWidget', () => {
expect(rect_element.getAttribute('stroke-dasharray')).toEqual('5,25,25'); expect(rect_element.getAttribute('stroke-dasharray')).toEqual('5,25,25');
expect(rect_element.getAttribute('width')).toEqual('100'); expect(rect_element.getAttribute('width')).toEqual('100');
expect(rect_element.getAttribute('height')).toEqual('200'); expect(rect_element.getAttribute('height')).toEqual('200');
expect(rect_element.getAttribute('rx')).toEqual('0');
expect(rect_element.getAttribute('ry')).toEqual('0');
}); });
}); });

View File

@ -33,7 +33,9 @@ export class RectDrawingWidget implements DrawingShapeWidget {
.attr('stroke-width', (rect) => rect.stroke_width) .attr('stroke-width', (rect) => rect.stroke_width)
.attr('stroke-dasharray', (rect) => this.qtDasharrayFixer.fix(rect.stroke_dasharray)) .attr('stroke-dasharray', (rect) => this.qtDasharrayFixer.fix(rect.stroke_dasharray))
.attr('width', (rect) => rect.width) .attr('width', (rect) => rect.width)
.attr('height', (rect) => rect.height); .attr('height', (rect) => rect.height)
.attr('rx', (rect) => rect.rx)
.attr('ry', (rect) => rect.ry);
drawing.exit().remove(); drawing.exit().remove();
} }

View File

@ -42,7 +42,7 @@ describe('TextDrawingWidget', () => {
const text_element = drew.nodes()[0]; const text_element = drew.nodes()[0];
expect(text_element.innerHTML).toEqual('<tspan xml:space="preserve" x="0" dy="0em">THIS IS TEXT</tspan>'); expect(text_element.innerHTML).toEqual('<tspan xml:space="preserve" x="0" dy="0em">THIS IS TEXT</tspan>');
expect(text_element.getAttribute('fill')).toEqual('#000000'); expect(text_element.getAttribute('fill')).toEqual('#000000');
expect(text_element.getAttribute('style')).toEqual('font-family: "Noto Sans"; font-size: 11pt; font-weight: bold'); expect(text_element.getAttribute('style')).toEqual('font-family: "Arial"; font-size: 12pt; font-weight: bold');
expect(text_element.getAttribute('text-decoration')).toEqual('line-through'); expect(text_element.getAttribute('text-decoration')).toEqual('line-through');
}); });

View File

@ -90,16 +90,16 @@ export class InterfaceStatusWidget implements Widget {
.merge(status_started_enter) .merge(status_started_enter)
.attr('class', 'status_started') .attr('class', 'status_started')
.attr('width', (ls: LinkStatus) => { .attr('width', (ls: LinkStatus) => {
return ls.port.length * 8 + 10; return ls.port.length * 10 + 5;
}) })
.attr('height', 20) .attr('height', 20)
.attr('x', (ls: LinkStatus) => ls.x - 30) .attr('x', (ls: LinkStatus) => ls.x - 30)
.attr('y', (ls: LinkStatus) => ls.y - 10) .attr('y', (ls: LinkStatus) => ls.y - 10)
.attr('rx', 8) .attr('rx', 8)
.attr('ry', 8) .attr('ry', 8)
.style('fill', 'white') .style('fill', '#c7ffdf')
.attr('stroke', '#2ecc71') .attr('stroke', '#2ecc71')
.attr('stroke-width', 3); .attr('stroke-width', 2);
status_started.exit().remove(); status_started.exit().remove();
const status_started_label = link_group const status_started_label = link_group
.selectAll<SVGTextElement, LinkStatus>('text.status_started_label') .selectAll<SVGTextElement, LinkStatus>('text.status_started_label')
@ -111,7 +111,7 @@ export class InterfaceStatusWidget implements Widget {
.text((ls: LinkStatus) => ls.port) .text((ls: LinkStatus) => ls.port)
.attr('x', (ls: LinkStatus) => ls.x - 25) .attr('x', (ls: LinkStatus) => ls.x - 25)
.attr('y', (ls: LinkStatus) => ls.y + 5) .attr('y', (ls: LinkStatus) => ls.y + 5)
.attr('fill', `black`); .attr('fill', `#0e9647`);
status_started_label.exit().remove(); status_started_label.exit().remove();
const status_stopped = link_group const status_stopped = link_group
@ -122,16 +122,16 @@ export class InterfaceStatusWidget implements Widget {
.merge(status_stopped_enter) .merge(status_stopped_enter)
.attr('class', 'status_stopped') .attr('class', 'status_stopped')
.attr('width', (ls: LinkStatus) => { .attr('width', (ls: LinkStatus) => {
return ls.port.length * 8 + 10; return ls.port.length * 10 + 5;
}) })
.attr('height', 20) .attr('height', 20)
.attr('x', (ls: LinkStatus) => ls.x - 30) .attr('x', (ls: LinkStatus) => ls.x - 30)
.attr('y', (ls: LinkStatus) => ls.y - 10) .attr('y', (ls: LinkStatus) => ls.y - 10)
.attr('rx', 8) .attr('rx', 8)
.attr('ry', 8) .attr('ry', 8)
.style('fill', 'white') .style('fill', '#ffe3e3')
.attr('stroke', 'red') .attr('stroke', 'red')
.attr('stroke-width', 3); .attr('stroke-width', 2);
status_stopped.exit().remove(); status_stopped.exit().remove();
const status_stopped_label = link_group const status_stopped_label = link_group
.selectAll<SVGTextElement, LinkStatus>('text.status_stopped_label') .selectAll<SVGTextElement, LinkStatus>('text.status_stopped_label')
@ -143,7 +143,7 @@ export class InterfaceStatusWidget implements Widget {
.text((ls: LinkStatus) => ls.port) .text((ls: LinkStatus) => ls.port)
.attr('x', (ls: LinkStatus) => ls.x - 25) .attr('x', (ls: LinkStatus) => ls.x - 25)
.attr('y', (ls: LinkStatus) => ls.y + 5) .attr('y', (ls: LinkStatus) => ls.y + 5)
.attr('fill', `black`); .attr('fill', `red`);
status_stopped_label.exit().remove(); status_stopped_label.exit().remove();
const status_suspended = link_group const status_suspended = link_group
@ -154,7 +154,7 @@ export class InterfaceStatusWidget implements Widget {
.merge(status_suspended_enter) .merge(status_suspended_enter)
.attr('class', 'status_suspended') .attr('class', 'status_suspended')
.attr('width', (ls: LinkStatus) => { .attr('width', (ls: LinkStatus) => {
return ls.port.length * 8 + 10; return ls.port.length * 10 + 5;
}) })
.attr('height', 20) .attr('height', 20)
.attr('x', (ls: LinkStatus) => ls.x - 30) .attr('x', (ls: LinkStatus) => ls.x - 30)
@ -162,8 +162,8 @@ export class InterfaceStatusWidget implements Widget {
.attr('rx', 8) .attr('rx', 8)
.attr('ry', 8) .attr('ry', 8)
.style('fill', 'white') .style('fill', 'white')
.attr('stroke', '#FFFF00') .attr('stroke', '#fffbc3')
.attr('stroke-width', 3); .attr('stroke-width', 2);
status_suspended.exit().remove(); status_suspended.exit().remove();
const status_suspended_label = link_group const status_suspended_label = link_group
.selectAll<SVGTextElement, LinkStatus>('text.status_suspended_label') .selectAll<SVGTextElement, LinkStatus>('text.status_suspended_label')
@ -175,7 +175,7 @@ export class InterfaceStatusWidget implements Widget {
.text((ls: LinkStatus) => ls.port) .text((ls: LinkStatus) => ls.port)
.attr('x', (ls: LinkStatus) => ls.x - 25) .attr('x', (ls: LinkStatus) => ls.x - 25)
.attr('y', (ls: LinkStatus) => ls.y + 5) .attr('y', (ls: LinkStatus) => ls.y + 5)
.attr('fill', `black`); .attr('fill', `#6b5633`);
status_suspended_label.exit().remove(); status_suspended_label.exit().remove();
} else { } else {
const status_started = link_group const status_started = link_group

View File

@ -87,6 +87,7 @@ export class LinkWidget implements Widget {
.filter((l) => { .filter((l) => {
return ( return (
!l.capturing && !l.capturing &&
!l.suspend &&
(l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss) (l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)
); );
}) })
@ -113,9 +114,7 @@ export class LinkWidget implements Widget {
link_body link_body
.filter((l) => { .filter((l) => {
return ( return (
l.capturing && l.suspend
l.suspend &&
!(l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)
); );
}) })
.append<SVGGElement>('g') .append<SVGGElement>('g')

View File

@ -13,8 +13,8 @@
<th mat-header-cell *matHeaderCellDef>Adapter type</th> <th mat-header-cell *matHeaderCellDef>Adapter type</th>
<td mat-cell *matCellDef="let element; let i = index"> <td mat-cell *matCellDef="let element; let i = index">
<mat-select placeholder="Type" [(ngModel)]="element.adapter_type"> <mat-select placeholder="Type" [(ngModel)]="element.adapter_type">
<mat-option *ngFor="let type of networkTypes" [value]="type"> <mat-option *ngFor="let type of networkTypes" [value]="type.value">
{{ type }} {{ type.name }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</td> </td>

View File

@ -98,14 +98,14 @@
<mat-checkbox [(ngModel)]="iouTemplate.l1_keepalives"> <mat-checkbox [(ngModel)]="iouTemplate.l1_keepalives">
Enable layer 1 keepalive messages (non-functional) </mat-checkbox Enable layer 1 keepalive messages (non-functional) </mat-checkbox
><br /> ><br />
<mat-checkbox [(ngModel)]="defaultSettings"> Use default IOU values for memories </mat-checkbox> <mat-checkbox [(ngModel)]="iouTemplate.use_default_iou_values"> Use default IOU values for memories </mat-checkbox>
<mat-form-field class="form-field" *ngIf="!defaultSettings"> <mat-form-field class="form-field" *ngIf="!iouTemplate.use_default_iou_values">
<input matInput type="number" [(ngModel)]="iouTemplate.ram" placeholder="RAM size" /> <input matInput type="number" [(ngModel)]="iouTemplate.ram" placeholder="RAM size" />
<span matSuffix>MB</span> <span matSuffix>MB</span>
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field" *ngIf="!defaultSettings"> <mat-form-field class="form-field" *ngIf="!iouTemplate.use_default_iou_values">
<input matInput type="number" [(ngModel)]="iouTemplate.nvram" placeholder="NVRAM size" /> <input matInput type="number" [(ngModel)]="iouTemplate.nvram" placeholder="NVRAM size" />
<span matSuffix>MB</span> <span matSuffix>KB</span>
</mat-form-field> </mat-form-field>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel> <mat-expansion-panel>

View File

@ -110,6 +110,9 @@
placeholder="Please enter name" placeholder="Please enter name"
/> />
</mat-form-field> </mat-form-field>
<div *ngIf="uploadedFile">
<mat-progress-bar mode="determinate" [value]="uploadProgress" aria-valuemin="0" aria-valuemax="100"></mat-progress-bar>
</div>
</div> </div>
</form> </form>
</mat-step> </mat-step>

View File

@ -32,6 +32,8 @@ export class AddQemuVmTemplateComponent implements OnInit {
chosenImage: string = ''; chosenImage: string = '';
qemuTemplate: QemuTemplate; qemuTemplate: QemuTemplate;
uploader: FileUploader; uploader: FileUploader;
uploadedFile: boolean = false;
uploadProgress: number = 0;
nameForm: FormGroup; nameForm: FormGroup;
memoryForm: FormGroup; memoryForm: FormGroup;
@ -86,6 +88,9 @@ export class AddQemuVmTemplateComponent implements OnInit {
}); });
this.toasterService.success('Image uploaded'); this.toasterService.success('Image uploaded');
}; };
this.uploader.onProgressItem = (progress: any) => {
this.uploadProgress = progress['progress'];
};
const server_id = this.route.snapshot.paramMap.get('server_id'); const server_id = this.route.snapshot.paramMap.get('server_id');
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => { this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
@ -127,6 +132,7 @@ export class AddQemuVmTemplateComponent implements OnInit {
} }
uploadImageFile(event) { uploadImageFile(event) {
this.uploadedFile = true;
let name = event.target.files[0].name; let name = event.target.files[0].name;
this.diskForm.controls['fileName'].setValue(name); this.diskForm.controls['fileName'].setValue(name);

View File

@ -186,13 +186,15 @@
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field"> <mat-form-field class="form-field">
<mat-select placeholder="Type" [(ngModel)]="qemuTemplate.adapter_type"> <mat-select placeholder="Type" [(ngModel)]="qemuTemplate.adapter_type">
<mat-option *ngFor="let type of networkTypes" [value]="type[0]"> {{ type[1] }} ({{ type[0] }}) </mat-option> <mat-option *ngFor="let type of networkTypes" [value]="type.value">{{type.name}} ({{type.value}}) </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<button mat-button class="configButton" (click)="setCustomAdaptersConfiguratorState(true)"> <button mat-button class="configButton" (click)="setCustomAdaptersConfiguratorState(true)">
Configure custom adapters</button Configure custom adapters</button
><br /> >
<mat-checkbox [(ngModel)]="qemuTemplate.legacy_networking"> Use the legacy networking mode </mat-checkbox> <br /><mat-checkbox [(ngModel)]="qemuTemplate.legacy_networking"> Use the legacy networking mode </mat-checkbox>
<br /><mat-checkbox [(ngModel)]="qemuTemplate.replicate_network_connection_state"> Replicate network connection state </mat-checkbox>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
@ -271,6 +273,8 @@
<input matInput type="text" [(ngModel)]="qemuTemplate.options" placeholder="Options" /> <input matInput type="text" [(ngModel)]="qemuTemplate.options" placeholder="Options" />
</mat-form-field> </mat-form-field>
<mat-checkbox [(ngModel)]="qemuTemplate.linked_clone"> Use as a linked base VM </mat-checkbox> <mat-checkbox [(ngModel)]="qemuTemplate.linked_clone"> Use as a linked base VM </mat-checkbox>
<br /><mat-checkbox [(ngModel)]="qemuTemplate.tpm"> Enable the Trusted Platform Module (TPM)</mat-checkbox>
<br /><mat-checkbox [(ngModel)]="qemuTemplate.uefi"> Enable the UEFI boot mode </mat-checkbox>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</mat-expansion-panel> </mat-expansion-panel>

View File

@ -2,3 +2,11 @@
<mat-icon>web_asset</mat-icon> <mat-icon>web_asset</mat-icon>
<span>Console</span> <span>Console</span>
</button> </button>
<button
mat-menu-item
*ngIf="node.node_type === 'docker' || node.node_type === 'dynamips' || node.node_type === 'qemu'"
(click)="openConsole(auxiliary=true)"
>
<mat-icon>web_asset</mat-icon>
<span>Auxiliary console</span>
</button>

View File

@ -1,8 +1,12 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Node } from '../../../../../cartography/models/node'; import { Node } from '../../../../../cartography/models/node';
import { Server } from '../../../../../models/server'; import { Server } from '../../../../../models/server';
import { NodeService } from '../../../../../services/node.service'; import { NodeService } from '../../../../../services/node.service';
import { ToasterService } from '../../../../../services/toaster.service'; import { ToasterService } from '../../../../../services/toaster.service';
import { ProtocolHandlerService } from '../../../../../services/protocol-handler.service';
import * as ipaddr from 'ipaddr.js';
@Component({ @Component({
selector: 'app-console-device-action-browser', selector: 'app-console-device-action-browser',
@ -12,16 +16,16 @@ export class ConsoleDeviceActionBrowserComponent {
@Input() server: Server; @Input() server: Server;
@Input() node: Node; @Input() node: Node;
constructor(private toasterService: ToasterService, private nodeService: NodeService) {} constructor(private toasterService: ToasterService, private nodeService: NodeService, private protocolHandlerService: ProtocolHandlerService) {}
openConsole() { openConsole(auxiliary: boolean = false) {
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => { this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
this.node = node; this.node = node;
this.startConsole(); this.startConsole(auxiliary);
}); });
} }
startConsole() { startConsole(auxiliary: boolean) {
if (this.node.status !== 'started') { if (this.node.status !== 'started') {
this.toasterService.error('This node must be started before a console can be opened'); this.toasterService.error('This node must be started before a console can be opened');
} else { } else {
@ -33,20 +37,41 @@ export class ConsoleDeviceActionBrowserComponent {
this.node.console_host = this.server.host; this.node.console_host = this.server.host;
} }
if ( try {
this.node.console_type === 'telnet' || var uri;
this.node.console_type === 'vnc' || var host = this.node.console_host;
this.node.console_type === 'spice' if (ipaddr.IPv6.isValid(host)) {
) { host = `[${host}]`;
try {
location.assign(
`gns3+${this.node.console_type}://${this.node.console_host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`
);
} catch (e) {
this.toasterService.error(e);
} }
} else { if (this.node.console_type === 'telnet') {
this.toasterService.error('Supported console types: telnet, vnc, spice.');
var console_port;
if (auxiliary === true) {
console_port = this.node.properties.aux;
if (console_port === undefined) {
this.toasterService.error('Auxiliary console port is not set.');
return;
}
} else {
console_port = this.node.console;
}
uri = `gns3+telnet://${host}:${console_port}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`;
} else if (this.node.console_type === 'vnc') {
uri = `gns3+vnc://${host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`;
} else if (this.node.console_type.startsWith('spice')) {
uri = `gns3+spice://${host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`
} else if (this.node.console_type.startsWith('http')) {
uri = `${this.node.console_type}://${host}:${this.node.console}`
return window.open(uri); // open an http console directly in a new window/tab
} else {
this.toasterService.error('Supported console types are: telnet, vnc, spice and spice+agent.');
return;
}
this.protocolHandlerService.open(uri);
} catch (e) {
this.toasterService.error(e);
} }
} }
} }

View File

@ -29,7 +29,7 @@ export class ConsoleDeviceActionComponent implements OnInit {
let consoleCommand = this.settingsService.getConsoleSettings() let consoleCommand = this.settingsService.getConsoleSettings()
? this.settingsService.getConsoleSettings() ? this.settingsService.getConsoleSettings()
: this.nodeService.getDefaultCommand(); : this.nodeService.getDefaultCommand();
const startedNodes = this.nodes.filter((node) => node.status === 'started'); const startedNodes = this.nodes.filter((node) => node.status === 'started' && node.console_type !== 'none');
if (startedNodes.length === 0) { if (startedNodes.length === 0) {
this.toasterService.error('Device needs to be started in order to console to it.'); this.toasterService.error('Device needs to be started in order to console to it.');
@ -37,7 +37,7 @@ export class ConsoleDeviceActionComponent implements OnInit {
} }
for (var node of this.nodes) { for (var node of this.nodes) {
if (node.status !== 'started') { if (node.status !== 'started' && node.console_type !== 'none') {
continue; continue;
} }

View File

@ -2,7 +2,7 @@
<div class="modal-form-container"> <div class="modal-form-container">
<form [formGroup]="formGroup"> <form [formGroup]="formGroup">
<mat-form-field class="form-field"> <mat-form-field class="form-field" *ngIf="element.fill !== undefined">
<input <input
matInput matInput
[ngModelOptions]="{ standalone: true }" [ngModelOptions]="{ standalone: true }"
@ -23,7 +23,13 @@
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field"> <mat-form-field class="form-field">
<input matInput formControlName="borderWidth" placeholder="Border width" type="number" /> <input
matInput formControlName="borderWidth"
placeholder="Border width"
type="number"
min="0"
max="100"
/>
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field" *ngIf="element.stroke_dasharray"> <mat-form-field class="form-field" *ngIf="element.stroke_dasharray">
@ -36,6 +42,41 @@
/> />
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field" *ngIf="element.width !== undefined">
<input
matInput
[ngModelOptions]="{ standalone: true }"
placeholder="Width"
min="0"
type="number"
[(ngModel)]="element.width"
/>
</mat-form-field>
<mat-form-field class="form-field" *ngIf="element.height !== undefined">
<input
matInput
[ngModelOptions]="{ standalone: true }"
placeholder="Height"
min="0"
type="number"
[(ngModel)]="element.height"
/>
</mat-form-field>
<mat-form-field class="form-field" *ngIf="element.rx !== undefined">
<input
matInput
[ngModelOptions]="{ standalone: true }"
placeholder="Corner radius"
type="number"
min="0"
max="100"
[(ngModel)]="element.rx"
/>
</mat-form-field>
<mat-form-field class="form-field"> <mat-form-field class="form-field">
<input matInput formControlName="rotation" placeholder="Rotation" type="number" /> <input matInput formControlName="rotation" placeholder="Rotation" type="number" />
</mat-form-field> </mat-form-field>

View File

@ -49,6 +49,8 @@ export class StyleEditorDialogComponent implements OnInit {
if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) { if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) {
this.element.fill = this.drawing.element.fill; this.element.fill = this.drawing.element.fill;
this.element.width = this.drawing.element.width;
this.element.height = this.drawing.element.height;
this.element.stroke = this.drawing.element.stroke; this.element.stroke = this.drawing.element.stroke;
this.element.stroke_dasharray = this.drawing.element.stroke_dasharray; this.element.stroke_dasharray = this.drawing.element.stroke_dasharray;
this.element.stroke_width = this.drawing.element.stroke_width; this.element.stroke_width = this.drawing.element.stroke_width;
@ -58,6 +60,11 @@ export class StyleEditorDialogComponent implements OnInit {
this.element.stroke_width = this.drawing.element.stroke_width; this.element.stroke_width = this.drawing.element.stroke_width;
} }
if (this.drawing.element instanceof RectElement) {
this.element.rx = this.drawing.element.rx;
this.element.ry = this.drawing.element.ry;
}
if (this.element.stroke_width === undefined) this.element.stroke_width = 0; if (this.element.stroke_width === undefined) this.element.stroke_width = 0;
this.formGroup.controls['borderWidth'].setValue(this.element.stroke_width); this.formGroup.controls['borderWidth'].setValue(this.element.stroke_width);
this.formGroup.controls['rotation'].setValue(this.drawing.rotation); this.formGroup.controls['rotation'].setValue(this.drawing.rotation);
@ -74,6 +81,8 @@ export class StyleEditorDialogComponent implements OnInit {
if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) { if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) {
this.drawing.element.fill = this.element.fill; this.drawing.element.fill = this.element.fill;
this.drawing.element.width = this.element.width;
this.drawing.element.height = this.element.height;
this.drawing.element.stroke = this.element.stroke; this.drawing.element.stroke = this.element.stroke;
this.drawing.element.stroke_dasharray = this.element.stroke_dasharray; this.drawing.element.stroke_dasharray = this.element.stroke_dasharray;
this.drawing.element.stroke_width = this.element.stroke_width; this.drawing.element.stroke_width = this.element.stroke_width;
@ -83,6 +92,11 @@ export class StyleEditorDialogComponent implements OnInit {
this.drawing.element.stroke_width = this.element.stroke_width; this.drawing.element.stroke_width = this.element.stroke_width;
} }
if (this.drawing.element instanceof RectElement) {
this.drawing.element.rx = this.element.rx;
this.drawing.element.ry = this.element.rx; // set ry with rx because we don't have ry in the form
}
let mapDrawing = this.drawingToMapDrawingConverter.convert(this.drawing); let mapDrawing = this.drawingToMapDrawingConverter.convert(this.drawing);
mapDrawing.element = this.drawing.element; mapDrawing.element = this.drawing.element;
@ -100,7 +114,11 @@ export class StyleEditorDialogComponent implements OnInit {
export class ElementData { export class ElementData {
fill: string; fill: string;
width: number;
height: number;
stroke: string; stroke: string;
stroke_width: number; stroke_width: number;
stroke_dasharray: string; stroke_dasharray: string;
rx: number;
ry: number;
} }

View File

@ -91,6 +91,7 @@ export class ImportApplianceComponent implements OnInit {
template.console_auto_start = appliance.iou.console_auto_start; template.console_auto_start = appliance.iou.console_auto_start;
template.ethernet_adapters = appliance.iou.ethernet_adapters; template.ethernet_adapters = appliance.iou.ethernet_adapters;
template.l1_keepalives = appliance.iou.l1_keepalives; template.l1_keepalives = appliance.iou.l1_keepalives;
template.use_default_iou_values = appliance.iou.use_default_iou_values;
template.nvram = appliance.iou.nvram; template.nvram = appliance.iou.nvram;
template.ram = appliance.iou.ram; template.ram = appliance.iou.ram;
template.serial_adapters = appliance.iou.serial_adapters; template.serial_adapters = appliance.iou.serial_adapters;

View File

@ -6,6 +6,7 @@ import { MatMenuModule } from '@angular/material/menu';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { ToasterService } from '../../../services/toaster.service'; import { ToasterService } from '../../../services/toaster.service';
import { ProtocolHandlerService } from '../../../services/protocol-handler.service';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource'; import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
import { ProjectWebServiceHandler, WebServiceMessage } from '../../../handlers/project-web-service-handler'; import { ProjectWebServiceHandler, WebServiceMessage } from '../../../handlers/project-web-service-handler';
@ -38,6 +39,7 @@ describe('LogConsoleComponent', () => {
let nodeConsoleService: NodeConsoleService; let nodeConsoleService: NodeConsoleService;
let mapSettingsService: MapSettingsService; let mapSettingsService: MapSettingsService;
let toasterService: ToasterService; let toasterService: ToasterService;
let protocolHandlerService: ProtocolHandlerService;
let httpServer = new HttpServer({} as HttpClient, {} as ServerErrorHandler); let httpServer = new HttpServer({} as HttpClient, {} as ServerErrorHandler);
@ -52,6 +54,7 @@ describe('LogConsoleComponent', () => {
{ provide: HttpServer, useValue: httpServer }, { provide: HttpServer, useValue: httpServer },
NodeConsoleService, NodeConsoleService,
ToasterService, ToasterService,
ProtocolHandlerService,
MapSettingsService MapSettingsService
], ],
declarations: [LogConsoleComponent], declarations: [LogConsoleComponent],
@ -59,6 +62,7 @@ describe('LogConsoleComponent', () => {
}).compileComponents(); }).compileComponents();
toasterService = TestBed.inject(ToasterService); toasterService = TestBed.inject(ToasterService);
protocolHandlerService = TestBed.inject(ProtocolHandlerService);
mapSettingsService = TestBed.inject(MapSettingsService); mapSettingsService = TestBed.inject(MapSettingsService);
nodeConsoleService = TestBed.inject(NodeConsoleService); nodeConsoleService = TestBed.inject(NodeConsoleService);
})); }));

View File

@ -23,9 +23,11 @@ import { Server } from '../../../models/server';
import { HttpServer } from '../../../services/http-server.service'; import { HttpServer } from '../../../services/http-server.service';
import { NodeService } from '../../../services/node.service'; import { NodeService } from '../../../services/node.service';
import { NodeConsoleService } from '../../../services/nodeConsole.service'; import { NodeConsoleService } from '../../../services/nodeConsole.service';
import { ProtocolHandlerService } from '../../../services/protocol-handler.service';
import { ThemeService } from '../../../services/theme.service'; import { ThemeService } from '../../../services/theme.service';
import { version } from '../../../version'; import { version } from '../../../version';
import { LogEventsDataSource } from './log-events-datasource'; import { LogEventsDataSource } from './log-events-datasource';
import * as ipaddr from 'ipaddr.js';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@ -69,6 +71,7 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
private projectWebServiceHandler: ProjectWebServiceHandler, private projectWebServiceHandler: ProjectWebServiceHandler,
private nodeService: NodeService, private nodeService: NodeService,
private nodesDataSource: NodesDataSource, private nodesDataSource: NodesDataSource,
private protocolHandlerService: ProtocolHandlerService,
private logEventsDataSource: LogEventsDataSource, private logEventsDataSource: LogEventsDataSource,
private httpService: HttpServer, private httpService: HttpServer,
private themeService: ThemeService, private themeService: ThemeService,
@ -224,20 +227,26 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
} else if (this.regexConsole.test(this.command)) { } else if (this.regexConsole.test(this.command)) {
if (node.status === 'started') { if (node.status === 'started') {
this.showCommand(`Launching console for node ${splittedCommand[1]}...`); this.showCommand(`Launching console for node ${splittedCommand[1]}...`);
var host = node.console_host;
if (ipaddr.IPv6.isValid(host)) {
host = `[${host}]`;
}
if (node.console_type === 'telnet') { if (node.console_type === 'telnet') {
location.assign( this.protocolHandlerService.open(
`gns3+telnet://${node.console_host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}` `gns3+telnet://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
); );
} else if (node.console_type === 'vnc') { } else if (node.console_type === 'vnc') {
location.assign( this.protocolHandlerService.open(
`gns3+vnc://${node.console_host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}` `gns3+vnc://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
); );
} else if (node.console_type === 'spice') { } else if (node.console_type.startsWith('spice')) {
location.assign( this.protocolHandlerService.open(
`gns3+spice://${node.console_host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}` `gns3+spice://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
); );
} else if (node.console_type.startsWith('http')) {
window.open(`${node.console_type}://${host}:${node.console}`);
} else { } else {
this.showCommand('Supported console types: telnet, vnc, spice.'); this.showCommand('Supported console types are: telnet, vnc, spice and spice+agent');
} }
} else { } else {
this.showCommand(`This node must be started before a console can be opened.`); this.showCommand(`This node must be started before a console can be opened.`);
@ -297,28 +306,28 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
printNode(node: Node): string { printNode(node: Node): string {
return ( return (
`command_line: ${node.command_line}, `command_line: ${node.command_line},
compute_id: ${node.compute_id}, compute_id: ${node.compute_id},
console: ${node.console}, console: ${node.console},
console_host: ${node.console_host}, console_host: ${node.console_host},
console_type: ${node.console_type}, console_type: ${node.console_type},
first_port_name: ${node.first_port_name}, first_port_name: ${node.first_port_name},
height: ${node.height}, height: ${node.height},
label: ${node.label.text}, label: ${node.label.text},
name: ${node.name}, name: ${node.name},
node_directory: ${node.node_directory}, node_directory: ${node.node_directory},
node_id: ${node.node_id}, node_id: ${node.node_id},
node_type: ${node.node_type}, node_type: ${node.node_type},
port_name_format: ${node.port_name_format}, port_name_format: ${node.port_name_format},
port_segment_size: ${node.port_segment_size}, ` + port_segment_size: ${node.port_segment_size}, ` +
this.printPorts(node.ports) + this.printPorts(node.ports) +
`project_id: ${node.project_id}, `project_id: ${node.project_id},
status: ${node.status}, status: ${node.status},
symbol: ${node.symbol}, symbol: ${node.symbol},
symbol_url: ${node.symbol_url}, symbol_url: ${node.symbol_url},
width: ${node.width}, width: ${node.width},
x: ${node.x}, x: ${node.x},
y: ${node.y}, y: ${node.y},
z: ${node.z}` z: ${node.z}`
); );
} }
@ -328,31 +337,31 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
ports.forEach((port) => { ports.forEach((port) => {
response = response =
response + response +
`adapter_number: ${port.adapter_number}, `adapter_number: ${port.adapter_number},
link_type: ${port.link_type}, link_type: ${port.link_type},
name: ${port.name}, name: ${port.name},
port_number: ${port.port_number}, port_number: ${port.port_number},
short_name: ${port.short_name}, `; short_name: ${port.short_name}, `;
}); });
return response; return response;
} }
printLink(link: Link): string { printLink(link: Link): string {
return `capture_file_name: ${link.capture_file_name}, return `capture_file_name: ${link.capture_file_name},
capture_file_path: ${link.capture_file_path}, capture_file_path: ${link.capture_file_path},
capturing: ${link.capturing}, capturing: ${link.capturing},
link_id: ${link.link_id}, link_id: ${link.link_id},
link_type: ${link.link_type}, link_type: ${link.link_type},
project_id: ${link.project_id}, project_id: ${link.project_id},
suspend: ${link.suspend}, `; suspend: ${link.suspend}, `;
} }
printDrawing(drawing: Drawing): string { printDrawing(drawing: Drawing): string {
return `drawing_id: ${drawing.drawing_id}, return `drawing_id: ${drawing.drawing_id},
project_id: ${drawing.project_id}, project_id: ${drawing.project_id},
rotation: ${drawing.rotation}, rotation: ${drawing.rotation},
x: ${drawing.x}, x: ${drawing.x},
y: ${drawing.y}, y: ${drawing.y},
z: ${drawing.z}`; z: ${drawing.z}`;
} }
} }

View File

@ -77,7 +77,7 @@
</ng-container> </ng-container>
<!-- <ng-container matColumnDef="expandedDetail"> <!-- <ng-container matColumnDef="expandedDetail">
<mat-cell *matCellDef="let detail"> <mat-cell *matCellDef="let detail">
The symbol for {{detail.element}} The symbol for {{detail.element}}
</mat-cell> </mat-cell>
</ng-container> --> </ng-container> -->
@ -85,14 +85,14 @@
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row> <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
<!-- <mat-row <!-- <mat-row
*matRowDef="let row; columns: displayedColumns;" *matRowDef="let row; columns: displayedColumns;"
matRipple matRipple
class="element-row" class="element-row"
[class.expanded]="expandedElement == row" [class.expanded]="expandedElement == row"
(click)="expandedElement = row"> (click)="expandedElement = row">
</mat-row> </mat-row>
<mat-row <mat-row
*matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow" *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'" [@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
style="overflow: hidden"> style="overflow: hidden">
@ -196,6 +196,38 @@
</div> </div>
</div> </div>
<div class="list-item-inside" *ngIf="version.images.bios_image">
<span>
{{ version.images.bios_image }}
</span>
<div>
<span *ngIf="checkImageFromVersion(version.images.bios_image)"
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span
>
<span *ngIf="!checkImageFromVersion(version.images.bios_image)"
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span
>
<input
type="file"
class="non-visible"
#fileBios
(change)="importImage($event, version.images.bios_image)"
ng2FileSelect
[uploader]="uploaderImage"
/>
<button class="button" mat-raised-button (click)="fileBios.click()">Import</button>
<button
class="button"
mat-raised-button
(click)="downloadImageFromVersion(version.images.bios_image)"
>
Download
</button>
</div>
</div>
<div class="list-item-inside" *ngIf="version.images.hda_disk_image"> <div class="list-item-inside" *ngIf="version.images.hda_disk_image">
<span> <span>
{{ version.images.hda_disk_image }} {{ version.images.hda_disk_image }}
@ -260,6 +292,70 @@
</div> </div>
</div> </div>
<div class="list-item-inside" *ngIf="version.images.hdc_disk_image">
<span>
{{ version.images.hdb_disk_image }}
</span>
<div>
<span *ngIf="checkImageFromVersion(version.images.hdc_disk_image)"
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span
>
<span *ngIf="!checkImageFromVersion(version.images.hdc_disk_image)"
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span
>
<input
type="file"
class="non-visible"
#file4
(change)="importImage($event, version.images.hdc_disk_image)"
ng2FileSelect
[uploader]="uploaderImage"
/>
<button class="button" mat-raised-button (click)="file4.click()">Import</button>
<button
class="button"
mat-raised-button
(click)="downloadImageFromVersion(version.images.hdc_disk_image)"
>
Download
</button>
</div>
</div>
<div class="list-item-inside" *ngIf="version.images.hdd_disk_image">
<span>
{{ version.images.hdd_disk_image }}
</span>
<div>
<span *ngIf="checkImageFromVersion(version.images.hdd_disk_image)"
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span
>
<span *ngIf="!checkImageFromVersion(version.images.hdd_disk_image)"
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span
>
<input
type="file"
class="non-visible"
#file5
(change)="importImage($event, version.images.hdd_disk_image)"
ng2FileSelect
[uploader]="uploaderImage"
/>
<button class="button" mat-raised-button (click)="file5.click()">Import</button>
<button
class="button"
mat-raised-button
(click)="downloadImageFromVersion(version.images.hdd_disk_image)"
>
Download
</button>
</div>
</div>
<div class="list-item-inside" *ngIf="version.images.cdrom_image"> <div class="list-item-inside" *ngIf="version.images.cdrom_image">
<span> <span>
{{ version.images.cdrom_image}} {{ version.images.cdrom_image}}
@ -276,12 +372,12 @@
<input <input
type="file" type="file"
class="non-visible" class="non-visible"
#file4 #file6
(change)="importImage($event, version.images.cdrom_image)" (change)="importImage($event, version.images.cdrom_image)"
ng2FileSelect ng2FileSelect
[uploader]="uploaderImage" [uploader]="uploaderImage"
/> />
<button class="button" mat-raised-button (click)="file4.click()">Import</button> <button class="button" mat-raised-button (click)="file6.click()">Import</button>
<button <button
class="button" class="button"
mat-raised-button mat-raised-button

View File

@ -320,17 +320,17 @@ export class NewTemplateDialogComponent implements OnInit {
setTimeout(() => { setTimeout(() => {
if (this.qemuBinaries.length) { if (this.qemuBinaries.length) {
if (this.applianceToInstall.qemu.arch === 'x86_64') { if (this.applianceToInstall.qemu.arch === 'x86_64') {
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.includes('qemu-system-x86_64')); let filtered_binaries = this.qemuBinaries.filter((n) => n.path.endsWith('qemu-system-x86_64'));
if (filtered_binaries.length) { if (filtered_binaries.length) {
this.selectedBinary = filtered_binaries[0]; this.selectedBinary = filtered_binaries[0];
} }
} else if (this.applianceToInstall.qemu.arch === 'i386') { } else if (this.applianceToInstall.qemu.arch === 'i386') {
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.includes('qemu-system-i386')); let filtered_binaries = this.qemuBinaries.filter((n) => n.path.endsWith('qemu-system-i386'));
if (filtered_binaries.length) { if (filtered_binaries.length) {
this.selectedBinary = filtered_binaries[0]; this.selectedBinary = filtered_binaries[0];
} }
} else if (this.applianceToInstall.qemu.arch === 'x86_64') { } else if (this.applianceToInstall.qemu.arch === 'arm') {
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.includes('qemu-system-arm')); let filtered_binaries = this.qemuBinaries.filter((n) => n.path.endsWith('qemu-system-arm'));
if (filtered_binaries.length) { if (filtered_binaries.length) {
this.selectedBinary = filtered_binaries[0]; this.selectedBinary = filtered_binaries[0];
} }
@ -364,7 +364,7 @@ export class NewTemplateDialogComponent implements OnInit {
autoFocus: false, autoFocus: false,
disableClose: true, disableClose: true,
}); });
dialogRef.componentInstance.confirmationMessage = `This is not the correct file. dialogRef.componentInstance.confirmationMessage = `This is not the correct file.
The MD5 sum is ${output} and should be ${imageToInstall.md5sum}. Do you want to accept it at your own risks?`; The MD5 sum is ${output} and should be ${imageToInstall.md5sum}. Do you want to accept it at your own risks?`;
dialogRef.afterClosed().subscribe((answer: boolean) => { dialogRef.afterClosed().subscribe((answer: boolean) => {
if (answer) { if (answer) {
@ -401,7 +401,8 @@ export class NewTemplateDialogComponent implements OnInit {
this.progressService.activate(); this.progressService.activate();
}; };
fileReader.readAsText(file); //fileReader.readAsText(file); //web browser out ouf memory when upload large image file
fileReader.onloadend(undefined);
} }
checkImageFromVersion(image: string): boolean { checkImageFromVersion(image: string): boolean {
@ -485,7 +486,7 @@ export class NewTemplateDialogComponent implements OnInit {
iouTemplate.startup_config = this.applianceToInstall.iou.startup_config; iouTemplate.startup_config = this.applianceToInstall.iou.startup_config;
iouTemplate.builtin = this.applianceToInstall.builtin; iouTemplate.builtin = this.applianceToInstall.builtin;
iouTemplate.category = this.getCategory(); iouTemplate.category = this.getCategory();
iouTemplate.default_name_format = this.applianceToInstall.port_name_format; iouTemplate.default_name_format = this.applianceToInstall.default_name_format;
iouTemplate.symbol = this.applianceToInstall.symbol; iouTemplate.symbol = this.applianceToInstall.symbol;
iouTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local'; iouTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
iouTemplate.template_id = uuid(); iouTemplate.template_id = uuid();
@ -534,7 +535,7 @@ export class NewTemplateDialogComponent implements OnInit {
iosTemplate.slot7 = this.applianceToInstall.dynamips.slot7; iosTemplate.slot7 = this.applianceToInstall.dynamips.slot7;
iosTemplate.builtin = this.applianceToInstall.builtin; iosTemplate.builtin = this.applianceToInstall.builtin;
iosTemplate.category = this.getCategory(); iosTemplate.category = this.getCategory();
iosTemplate.default_name_format = this.applianceToInstall.port_name_format; iosTemplate.default_name_format = this.applianceToInstall.default_name_format;
iosTemplate.symbol = this.applianceToInstall.symbol; iosTemplate.symbol = this.applianceToInstall.symbol;
iosTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local'; iosTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
iosTemplate.template_id = uuid(); iosTemplate.template_id = uuid();
@ -572,7 +573,7 @@ export class NewTemplateDialogComponent implements OnInit {
dockerTemplate.console_type = this.applianceToInstall.docker.console_type; dockerTemplate.console_type = this.applianceToInstall.docker.console_type;
dockerTemplate.builtin = this.applianceToInstall.builtin; dockerTemplate.builtin = this.applianceToInstall.builtin;
dockerTemplate.category = this.getCategory(); dockerTemplate.category = this.getCategory();
dockerTemplate.default_name_format = this.applianceToInstall.port_name_format; dockerTemplate.default_name_format = this.applianceToInstall.default_name_format;
dockerTemplate.symbol = this.applianceToInstall.symbol; dockerTemplate.symbol = this.applianceToInstall.symbol;
dockerTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local'; dockerTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
dockerTemplate.template_id = uuid(); dockerTemplate.template_id = uuid();
@ -629,12 +630,17 @@ export class NewTemplateDialogComponent implements OnInit {
qemuTemplate.category = this.getCategory(); qemuTemplate.category = this.getCategory();
qemuTemplate.first_port_name = this.applianceToInstall.first_port_name; qemuTemplate.first_port_name = this.applianceToInstall.first_port_name;
qemuTemplate.port_name_format = this.applianceToInstall.port_name_format; qemuTemplate.port_name_format = this.applianceToInstall.port_name_format;
qemuTemplate.port_segment_size = this.applianceToInstall.port_segment_size;
qemuTemplate.default_name_format = this.applianceToInstall.default_name_format
qemuTemplate.symbol = this.applianceToInstall.symbol; qemuTemplate.symbol = this.applianceToInstall.symbol;
qemuTemplate.qemu_path = this.selectedBinary.path; qemuTemplate.qemu_path = this.selectedBinary.path;
qemuTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local'; qemuTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
qemuTemplate.template_id = uuid(); qemuTemplate.template_id = uuid();
qemuTemplate.hda_disk_image = version.images.hda_disk_image; qemuTemplate.hda_disk_image = version.images.hda_disk_image;
qemuTemplate.hdb_disk_image = version.images.hdb_disk_image; qemuTemplate.hdb_disk_image = version.images.hdb_disk_image;
qemuTemplate.hdc_disk_image = version.images.hdc_disk_image;
qemuTemplate.hdd_disk_image = version.images.hdd_disk_image;
qemuTemplate.cdrom_image = version.images.cdrom_image;
qemuTemplate.template_type = 'qemu'; qemuTemplate.template_type = 'qemu';
qemuTemplate.usage = this.applianceToInstall.usage; qemuTemplate.usage = this.applianceToInstall.usage;

View File

@ -101,7 +101,7 @@
<h6>Additional directories</h6> <h6>Additional directories</h6>
<mat-form-field class="form-field"> <mat-form-field class="form-field">
<textarea matInput type="text" [(ngModel)]="node.properties.extra_volumes"></textarea> <textarea matInput type="text" [(ngModel)]="additionalDirectories"></textarea>
</mat-form-field> </mat-form-field>
</mat-tab> </mat-tab>

View File

@ -20,13 +20,14 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
name: string; name: string;
generalSettingsForm: FormGroup; generalSettingsForm: FormGroup;
consoleTypes: string[] = []; consoleTypes: string[] = [];
consoleResolutions: string[] = ['640x480', '800x600', '1024x768', '1280x800', '1280x1024', '1366x768', '1920x1080']; consoleResolutions: string[] = ['2560x1440', '1920x1080', '1680x1050', '1440x900', '1366x768', '1280x1024', '1280x800', '1024x768', '800x600', '640x480'];
private conf = { private conf = {
autoFocus: false, autoFocus: false,
width: '800px', width: '800px',
disableClose: true, disableClose: true,
}; };
dialogRef; dialogRef;
additionalDirectories: string = "";
constructor( constructor(
public dialogReference: MatDialogRef<ConfiguratorDialogDockerComponent>, public dialogReference: MatDialogRef<ConfiguratorDialogDockerComponent>,
@ -39,7 +40,7 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
this.generalSettingsForm = this.formBuilder.group({ this.generalSettingsForm = this.formBuilder.group({
name: new FormControl('', Validators.required), name: new FormControl('', Validators.required),
adapter: new FormControl('', Validators.required), adapter: new FormControl('', Validators.required),
startCommand: new FormControl('', Validators.required), startCommand: new FormControl(''),
consoleHttpPort: new FormControl('', Validators.required), consoleHttpPort: new FormControl('', Validators.required),
consoleHttpPath: new FormControl('', Validators.required), consoleHttpPath: new FormControl('', Validators.required),
}); });
@ -50,6 +51,12 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
this.node = node; this.node = node;
this.name = node.name; this.name = node.name;
this.getConfiguration(); this.getConfiguration();
if (this.node.properties.extra_volumes && this.node.properties.extra_volumes.length>0) {
for (let index = 0; index < this.node.properties.extra_volumes.length - 1; index++) {
this.additionalDirectories = this.additionalDirectories + this.node.properties.extra_volumes[index] + "\n";
}
this.additionalDirectories = this.additionalDirectories + this.node.properties.extra_volumes[this.node.properties.extra_volumes.length - 1];
}
}); });
} }
@ -72,7 +79,16 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
} }
onSaveClick() { onSaveClick() {
var extraVolumes = this.additionalDirectories.split("\n").filter(elem => elem != "");
for (const item of extraVolumes) {
console.log(item);
if (!item.startsWith("/")) {
this.toasterService.error(`Wrong format for additional directories.`);
return;
}
}
if (this.generalSettingsForm.valid) { if (this.generalSettingsForm.valid) {
this.node.properties.extra_volumes = extraVolumes;
this.nodeService.updateNode(this.server, this.node).subscribe(() => { this.nodeService.updateNode(this.server, this.node).subscribe(() => {
this.toasterService.success(`Node ${this.node.name} updated.`); this.toasterService.success(`Node ${this.node.name} updated.`);
this.onCancelClick(); this.onCancelClick();

View File

@ -20,13 +20,14 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox [(ngModel)]="node.console_auto_start"> Auto start console </mat-checkbox><br /> <mat-checkbox [(ngModel)]="node.console_auto_start"> Auto start console </mat-checkbox><br />
<mat-form-field class="form-field"> <mat-checkbox [(ngModel)]="node.properties.use_default_iou_values"> Use default IOU values for memories </mat-checkbox>
<mat-form-field class="form-field" *ngIf="!node.properties.use_default_iou_values">
<input matInput type="number" [(ngModel)]="node.properties.ram" placeholder="RAM size" /> <input matInput type="number" [(ngModel)]="node.properties.ram" placeholder="RAM size" />
<span matSuffix>MB</span> <span matSuffix>MB</span>
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field"> <mat-form-field class="form-field" *ngIf="!node.properties.use_default_iou_values">
<input matInput type="number" [(ngModel)]="node.properties.nvram" placeholder="NVRAM size" /> <input matInput type="number" [(ngModel)]="node.properties.nvram" placeholder="NVRAM size" />
<span matSuffix>MB</span> <span matSuffix>KB</span>
</mat-form-field> </mat-form-field>
</mat-tab> </mat-tab>
@ -38,7 +39,7 @@
matInput matInput
formControlName="ethernetAdapters" formControlName="ethernetAdapters"
type="number" type="number"
[(ngModel)]="node.ethernet_adapters" [(ngModel)]="node.properties.ethernet_adapters"
placeholder="Ethernet adapters" placeholder="Ethernet adapters"
/> />
</mat-form-field> </mat-form-field>
@ -47,7 +48,7 @@
matInput matInput
formControlName="serialAdapters" formControlName="serialAdapters"
type="number" type="number"
[(ngModel)]="node.serial_adapters" [(ngModel)]="node.properties.serial_adapters"
placeholder="Serial adapters" placeholder="Serial adapters"
/> />
</mat-form-field> </mat-form-field>

View File

@ -238,6 +238,12 @@
<mat-form-field class="form-field"> <mat-form-field class="form-field">
<input matInput type="text" [(ngModel)]="node.properties.options" placeholder="Options" /> <input matInput type="text" [(ngModel)]="node.properties.options" placeholder="Options" />
</mat-form-field> </mat-form-field>
<br /><mat-checkbox [(ngModel)]="node.properties.tpm">
Enable the Trusted Platform Module (TPM)
</mat-checkbox>
<br /><mat-checkbox [(ngModel)]="node.properties.uefi">
Enable the UEFI boot mode
</mat-checkbox>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</mat-tab> </mat-tab>

View File

@ -17,6 +17,7 @@ import { ToasterService } from '../../../../services/toaster.service';
import { MockedToasterService } from '../../../../services/toaster.service.spec'; import { MockedToasterService } from '../../../../services/toaster.service.spec';
import { MockedLinkService, MockedNodesDataSource } from '../../project-map.component.spec'; import { MockedLinkService, MockedNodesDataSource } from '../../project-map.component.spec';
import { StartCaptureDialogComponent } from './start-capture.component'; import { StartCaptureDialogComponent } from './start-capture.component';
import { ProtocolHandlerService } from '../../../../services/protocol-handler.service';
describe('StartCaptureDialogComponent', () => { describe('StartCaptureDialogComponent', () => {
let component: StartCaptureDialogComponent; let component: StartCaptureDialogComponent;
@ -25,6 +26,8 @@ describe('StartCaptureDialogComponent', () => {
let mockedToasterService = new MockedToasterService(); let mockedToasterService = new MockedToasterService();
let mockedLinkService = new MockedLinkService(); let mockedLinkService = new MockedLinkService();
let mockedNodesDataSource = new MockedNodesDataSource(); let mockedNodesDataSource = new MockedNodesDataSource();
let protocolHandlerService: ProtocolHandlerService;
let dialogRef = { let dialogRef = {
close: jasmine.createSpy('close'), close: jasmine.createSpy('close'),
}; };
@ -49,10 +52,13 @@ describe('StartCaptureDialogComponent', () => {
{ provide: LinkService, useValue: mockedLinkService }, { provide: LinkService, useValue: mockedLinkService },
{ provide: NodesDataSource, useValue: mockedNodesDataSource }, { provide: NodesDataSource, useValue: mockedNodesDataSource },
{ provide: PacketCaptureService }, { provide: PacketCaptureService },
ProtocolHandlerService,
], ],
declarations: [StartCaptureDialogComponent], declarations: [StartCaptureDialogComponent],
schemas: [NO_ERRORS_SCHEMA], schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); }).compileComponents();
protocolHandlerService = TestBed.inject(ProtocolHandlerService);
})); }));
beforeEach(() => { beforeEach(() => {

View File

@ -56,7 +56,7 @@ export class StartCaptureDialogComponent implements OnInit {
const sourcePort = sourceNode.ports[this.link.nodes[0].port_number]; const sourcePort = sourceNode.ports[this.link.nodes[0].port_number];
const targetPort = targetNode.ports[this.link.nodes[1].port_number]; const targetPort = targetNode.ports[this.link.nodes[1].port_number];
this.inputForm.controls['fileName'].setValue( this.inputForm.controls['fileName'].setValue(
`${sourceNode.name}_${sourcePort.name}_to_${targetNode.name}_${targetPort.name}` `${sourceNode.name}_${sourcePort.name}_to_${targetNode.name}_${targetPort.name}`.replace(new RegExp('[^0-9A-Za-z_-]', 'g'), '')
); );
} }

View File

@ -104,6 +104,13 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
} }
public addDrawing(selectedObject: string) { public addDrawing(selectedObject: string) {
if ((selectedObject === 'rectangle' && this.drawTools.isRectangleChosen) || (selectedObject === 'ellipse' && this.drawTools.isEllipseChosen) ||
(selectedObject === 'line' && this.drawTools.isLineChosen) || (selectedObject === 'text' && this.drawTools.isTextChosen)) {
document.documentElement.style.cursor = "default";
} else {
document.documentElement.style.cursor = "crosshair";
}
switch (selectedObject) { switch (selectedObject) {
case 'rectangle': case 'rectangle':
this.drawTools.isTextChosen = false; this.drawTools.isTextChosen = false;
@ -140,6 +147,8 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
} }
public resetDrawToolChoice() { public resetDrawToolChoice() {
document.documentElement.style.cursor = "default";
this.drawTools.isRectangleChosen = false; this.drawTools.isRectangleChosen = false;
this.drawTools.isEllipseChosen = false; this.drawTools.isEllipseChosen = false;
this.drawTools.isLineChosen = false; this.drawTools.isLineChosen = false;

View File

@ -67,6 +67,14 @@
</div> </div>
</div> </div>
<div> <div>
<button
class="map-settings-button"
matTooltip="Project Map Settings"
matTooltipClass="custom-tooltip"
mat-icon-button
[matMenuTriggerFor]="viewMenu">
<mat-icon>view_module</mat-icon>
</button>
<button <button
matTooltip="Toggle topology/servers summary" matTooltip="Toggle topology/servers summary"
matTooltipClass="custom-tooltip" matTooltipClass="custom-tooltip"
@ -83,39 +91,27 @@
<!-- GNS3 menu --> <!-- GNS3 menu -->
<mat-menu #mainMenu="matMenu" [overlapTrigger]="false"> <mat-menu #mainMenu="matMenu" [overlapTrigger]="false">
<button mat-menu-item [matMenuTriggerFor]="projectMenu">
<mat-icon>insert_drive_file</mat-icon>
<span>Project settings</span>
</button>
<button mat-menu-item [routerLink]="['/server', server.id, 'projects']"> <button mat-menu-item [routerLink]="['/server', server.id, 'projects']">
<mat-icon>work</mat-icon> <mat-icon>work</mat-icon>
<span>Go to projects</span> <span>Projects</span>
</button> </button>
<button mat-menu-item [routerLink]="['/servers']"> <button mat-menu-item [routerLink]="['/servers']">
<mat-icon>developer_board</mat-icon> <mat-icon>developer_board</mat-icon>
<span>Go to servers</span> <span>Servers</span>
</button>
<button mat-menu-item routerLink="/server/{{ server.id }}/preferences">
<mat-icon>settings_applications</mat-icon>
<span>Go to preferences</span>
</button> </button>
<button mat-menu-item routerLink="/server/{{ server.id }}/systemstatus"> <button mat-menu-item routerLink="/server/{{ server.id }}/systemstatus">
<mat-icon>info</mat-icon> <mat-icon>data_usage</mat-icon>
<span>Go to system status</span> <span>System Status</span>
</button> </button>
<button mat-menu-item routerLink="/settings"> <button mat-menu-item routerLink="/settings">
<mat-icon>settings</mat-icon> <mat-icon>settings</mat-icon>
<span>Go to settings</span> <span>Settings</span>
</button>
<button mat-menu-item (click)="addNewTemplate()">
<mat-icon>control_point</mat-icon>
<span>New template</span>
</button> </button>
<app-import-appliance [server]="server" [project]="project"></app-import-appliance> <app-import-appliance [server]="server" [project]="project"></app-import-appliance>
<button mat-menu-item [matMenuTriggerFor]="projectMenu">
<mat-icon>settings</mat-icon>
<span>Project settings</span>
</button>
<button mat-menu-item [matMenuTriggerFor]="viewMenu">
<mat-icon>view_module</mat-icon>
<span>Map settings</span>
</button>
</mat-menu> </mat-menu>
<!-- Project Settings sub-menu --> <!-- Project Settings sub-menu -->
@ -256,4 +252,4 @@
</div> </div>
</div> </div>
<ng-template #topologySummaryContainer></ng-template> <ng-template #topologySummaryContainer></ng-template>

View File

@ -73,6 +73,10 @@ g.node:hover {
font-size: 28px !important; font-size: 28px !important;
} }
.map-settings-button mat-icon {
font-size: 22px !important;
}
.selected { .selected {
background: rgba(0, 151, 167, 0.1); background: rgba(0, 151, 167, 0.1);

View File

@ -67,6 +67,7 @@ import { SymbolService } from '../../services/symbol.service';
import { ThemeService } from '../../services/theme.service'; import { ThemeService } from '../../services/theme.service';
import { ToasterService } from '../../services/toaster.service'; import { ToasterService } from '../../services/toaster.service';
import { ToolsService } from '../../services/tools.service'; import { ToolsService } from '../../services/tools.service';
import { ProtocolHandlerService } from '../../services/protocol-handler.service';
import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component'; import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component';
import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component'; import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component';
import { EditProjectDialogComponent } from '../projects/edit-project-dialog/edit-project-dialog.component'; import { EditProjectDialogComponent } from '../projects/edit-project-dialog/edit-project-dialog.component';
@ -173,8 +174,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
private title: Title, private title: Title,
private nodeConsoleService: NodeConsoleService, private nodeConsoleService: NodeConsoleService,
private symbolService: SymbolService, private symbolService: SymbolService,
private protocolHandlerService: ProtocolHandlerService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private cfr: ComponentFactoryResolver, private cfr: ComponentFactoryResolver,
private injector: Injector private injector: Injector
) {} ) {}
@ -229,7 +231,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.instance.instance.ngOnDestroy(); this.instance.instance.ngOnDestroy();
this.instance.destroy(); this.instance.destroy();
} }
} }
} }
addSubscriptions() { addSubscriptions() {
@ -334,7 +336,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.toggleShowTopologySummary(this.mapSettingsService.isTopologySummaryVisible); this.toggleShowTopologySummary(this.mapSettingsService.isTopologySummaryVisible);
this.recentlyOpenedProjectService.setServerId(this.server.id.toString()); this.recentlyOpenedProjectService.setServerId(this.server.id.toString());
this.recentlyOpenedProjectService.setProjectId(this.project.project_id);
if (this.project.status === 'opened') { if (this.project.status === 'opened') {
return new Observable<Project>((observer) => { return new Observable<Project>((observer) => {
@ -423,12 +424,22 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.toasterService.success('Node has been deleted'); this.toasterService.success('Node has been deleted');
}); });
}); });
}
selected
.filter((item) => item instanceof MapDrawing)
.forEach((item: MapDrawing) => {
const drawing = this.mapDrawingToDrawing.convert(item);
this.drawingService.delete(this.server, drawing).subscribe((data) => {
this.toasterService.success('Drawing has been deleted');
});
});
}
}); });
} }
onProjectLoad(project: Project) { onProjectLoad(project: Project) {
this.readonly = this.projectService.isReadOnly(project); this.readonly = this.projectService.isReadOnly(project);
this.recentlyOpenedProjectService.setProjectId(this.project.project_id);
const subscription = this.projectService const subscription = this.projectService
.nodes(this.server, project.project_id) .nodes(this.server, project.project_id)
@ -966,7 +977,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
) { ) {
this.toasterService.error('Project with running nodes cannot be exported.'); this.toasterService.error('Project with running nodes cannot be exported.');
} else { } else {
location.assign(this.projectService.getExportPath(this.server, this.project)); this.protocolHandlerService.open(this.projectService.getExportPath(this.server, this.project));
} }
} }
@ -981,8 +992,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
fileReader.onloadend = () => { fileReader.onloadend = () => {
let image = fileReader.result; let image = fileReader.result;
let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"
height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\" height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\"
xlink:href=\"${image}\"/>\n</svg>`; xlink:href=\"${image}\"/>\n</svg>`;
this.drawingService this.drawingService
.add(this.server, this.project.project_id, -(imageToUpload.width / 2), -(imageToUpload.height / 2), svg) .add(this.server, this.project.project_id, -(imageToUpload.width / 2), -(imageToUpload.height / 2), svg)

View File

@ -2,6 +2,7 @@
<div class="default-header"> <div class="default-header">
<div class="row"> <div class="row">
<h1 class="col">Projects</h1> <h1 class="col">Projects</h1>
<button class="col" mat-raised-button (click)="goToSystemStatus()" class="add-button">Go to system status</button>
<button class="col" mat-raised-button (click)="goToPreferences()" class="add-button">Go to preferences</button> <button class="col" mat-raised-button (click)="goToPreferences()" class="add-button">Go to preferences</button>
<button class="col" mat-raised-button color="primary" (click)="addBlankProject()" class="add-button"> <button class="col" mat-raised-button color="primary" (click)="addBlankProject()" class="add-button">
Add blank project Add blank project

View File

@ -89,6 +89,12 @@ export class ProjectsComponent implements OnInit {
.catch((error) => this.toasterService.error('Cannot navigate to the preferences')); .catch((error) => this.toasterService.error('Cannot navigate to the preferences'));
} }
goToSystemStatus() {
this.router
.navigate(['/server', this.server.id, 'systemstatus'])
.catch((error) => this.toasterService.error('Cannot navigate to the system status'));
}
refresh() { refresh() {
this.projectService.list(this.server).subscribe( this.projectService.list(this.server).subscribe(
(projects: Project[]) => { (projects: Project[]) => {

View File

@ -11,6 +11,7 @@
<div> <div>
<mat-checkbox [(ngModel)]="settings.crash_reports">Send anonymous crash reports</mat-checkbox><br /> <mat-checkbox [(ngModel)]="settings.crash_reports">Send anonymous crash reports</mat-checkbox><br />
<mat-checkbox [(ngModel)]="settings.anonymous_statistics">Send anonymous usage statistics</mat-checkbox><br />
<mat-checkbox [(ngModel)]="integrateLinksLabelsToLinks">Integrate link labels to links</mat-checkbox><br /> <mat-checkbox [(ngModel)]="integrateLinksLabelsToLinks">Integrate link labels to links</mat-checkbox><br />
<mat-checkbox [(ngModel)]="openConsolesInWidget">Open consoles in the widget instead of in new tabs after clicking start consoles for all nodes</mat-checkbox> <mat-checkbox [(ngModel)]="openConsolesInWidget">Open consoles in the widget instead of in new tabs after clicking start consoles for all nodes</mat-checkbox>
</div> </div>

View File

@ -69,6 +69,7 @@ describe('SettingsComponent', () => {
const settings = { const settings = {
crash_reports: true, crash_reports: true,
experimental_features: true, experimental_features: true,
anonymous_statistics: true,
angular_map: false, angular_map: false,
console_command: '', console_command: '',
}; };

View File

@ -1,5 +1,5 @@
<div class="title-container"> <div class="title-container">
<h1 mat-dialog-title>Add a node</h1> <h1 mat-dialog-title>Insert New Node</h1>
<button <button
mat-button mat-button
class="top-button" class="top-button"

View File

@ -10,63 +10,95 @@
</button> </button>
<mat-menu #mainMenu="matMenu"> <mat-menu #mainMenu="matMenu">
<button mat-menu-item (click)="openDialog()"> <div class="templateMenu">
<mat-icon>add_to_queue</mat-icon> <div class="templateMenuHeader">
<span>Open dialog to configure</span> <button mat-menu-item (click)="openDialog()">
</button> <mat-icon>add_to_queue</mat-icon>
<span>
Insert New Node...
</span>
</button>
<button mat-menu-item (click)="addNewTemplate()">
<mat-icon>library_add</mat-icon>
<span>
New Template...
</span>
</button>
</div>
<mat-form-field (click)="$event.stopPropagation()" class="form-field" floatPlaceholder="never"> <div class="templateFilterBar">
<input <mat-form-field (click)="$event.stopPropagation()" class="form-field" floatPlaceholder="never">
matInput <input
placeholder="Search by name" matInput
(keyup)="filterTemplates($event)" placeholder="Filter by name"
[(ngModel)]="searchText" search="search"
[ngModelOptions]="{ standalone: true }" (keyup)="filterTemplates($event)"
/> [(ngModel)]="searchText"
</mat-form-field> [ngModelOptions]="{ standalone: true }"
<mat-form-field (click)="$event.stopPropagation()" class="form-field"> />
<mat-select <mat-icon class="searchIcon" matSuffix>search</mat-icon>
[ngModelOptions]="{ standalone: true }" </mat-form-field>
placeholder="Filter templates by type" <mat-form-field (click)="$event.stopPropagation()" class="form-field">
(selectionChange)="filterTemplates($event)" <mat-select
[(ngModel)]="selectedType" [ngModelOptions]="{ standalone: true }"
> placeholder="Filter by type"
<mat-option *ngFor="let type of templateTypes" [value]="type"> (selectionChange)="filterTemplates($event)"
{{ type }} [(ngModel)]="selectedType"
</mat-option> >
</mat-select> <mat-option *ngFor="let type of templateTypes" [value]="type">
</mat-form-field> {{ type }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="menu"> <div class="menu">
<div class="templateList"> <div class="templateList">
<mat-list-item *ngFor="let template of filteredTemplates; let i = index"> <mat-list-item *ngFor="let template of filteredTemplates; let i = index">
<span *ngIf="i % 4 === 0" class="templateRow"> <span *ngIf="i % 4 === 0" class="templateRow">
<span class="templateIcon"> <span class="templateIcon">
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i])"> <div
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i])" /> mwlDraggable
</div> (dragStart)="dragStart($event)"
<div class="templateText">{{ filteredTemplates[i].name }}</div> (dragEnd)="dragEnd($event, filteredTemplates[i])"
</span> class="iconContainer">
<span *ngIf="filteredTemplates[i + 1]" class="templateIcon"> <img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i])" />
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 1])"> </div>
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 1])" /> <div class="templateText">{{ filteredTemplates[i].name }}</div>
</div> </span>
<div class="templateText">{{ filteredTemplates[i + 1].name }}</div> <span *ngIf="filteredTemplates[i + 1]" class="templateIcon">
</span> <div
<span *ngIf="filteredTemplates[i + 2]" class="templateIcon"> mwlDraggable
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 2])"> (dragStart)="dragStart($event)"
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 2])" /> (dragEnd)="dragEnd($event, filteredTemplates[i + 1])"
</div> class="iconContainer">
<div class="templateText">{{ filteredTemplates[i + 2].name }}</div> <img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 1])" />
</span> </div>
<span *ngIf="filteredTemplates[i + 3]" class="templateIcon"> <div class="templateText">{{ filteredTemplates[i + 1].name }}</div>
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 3])"> </span>
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 3])" /> <span *ngIf="filteredTemplates[i + 2]" class="templateIcon">
</div> <div
<div class="templateText">{{ filteredTemplates[i + 3].name }}</div> mwlDraggable
</span> (dragStart)="dragStart($event)"
</span> (dragEnd)="dragEnd($event, filteredTemplates[i + 2])"
</mat-list-item> class="iconContainer">
</div> <img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 2])" />
</div> </div>
</mat-menu> <div class="templateText">{{ filteredTemplates[i + 2].name }}</div>
</span>
<span *ngIf="filteredTemplates[i + 3]" class="templateIcon">
<div
mwlDraggable
(dragStart)="dragStart($event)"
(dragEnd)="dragEnd($event, filteredTemplates[i + 3])"
class="iconContainer">
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 3])" />
</div>
<div class="templateText">{{ filteredTemplates[i + 3].name }}</div>
</span>
</span>
</mat-list-item>
</div>
</div>
</div>
</mat-menu>

View File

@ -1,6 +1,7 @@
::ng-deep .mat-menu-panel { ::ng-deep .mat-menu-panel {
max-width: 400px; max-width: 400px;
max-height: 500px; max-height: 640px;
border-radius: 8px;
} }
.menu { .menu {
@ -8,6 +9,26 @@
overflow-y: scroll; overflow-y: scroll;
scrollbar-color: darkgrey #263238; scrollbar-color: darkgrey #263238;
scrollbar-width: thin; scrollbar-width: thin;
font-size: 12px;
}
.templateMenuHeader {
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.templateFilterBar {
padding: 10px 2%;
background-color: rgba(0,0,0,0.2);
margin-bottom: 10px;
}
.templateFilterBar > .form-field {
font-size: 12px;
}
.templateFilterBar .searchIcon {
position: relative;
top: 5px;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
@ -24,20 +45,22 @@
} }
.form-field { .form-field {
width: 90%; width: 44%;
margin-left: 5%; margin-left: 3%;
margin-right: 5%; margin-right: 3%;
} }
.image { .image {
width: 65px; display: inline-block;
height: 65px; width: 55px;
height: 55px;
filter: invert(0); filter: invert(0);
--webkit-filter: invert(0) !important; --webkit-filter: invert(0) !important;
} }
.templateList { .templateList {
width: 100%; width: 100%;
padding: 10px;
} }
.templateRow { .templateRow {
@ -50,6 +73,23 @@
} }
.templateIcon { .templateIcon {
width: 80px !important; width: 90px !important;
padding: 10px; padding: 2px 5px;
text-align: center;
margin: 3px;
}
.templateIcon > .iconContainer {
display: inline-flex;
align-items: center;
justify-content: center;
width: 70px;
height: 70px;
margin-bottom: 10px;
border-radius: 50%;
cursor: move;
}
.templateIcon > .iconContainer:hover {
background-color: rgba(237, 246, 231, 0.08);
} }

View File

@ -8,6 +8,7 @@ import { MapScaleService } from '../../services/mapScale.service';
import { SymbolService } from '../../services/symbol.service'; import { SymbolService } from '../../services/symbol.service';
import { TemplateService } from '../../services/template.service'; import { TemplateService } from '../../services/template.service';
import { NodeAddedEvent, TemplateListDialogComponent } from './template-list-dialog/template-list-dialog.component'; import { NodeAddedEvent, TemplateListDialogComponent } from './template-list-dialog/template-list-dialog.component';
import { NewTemplateDialogComponent } from '../project-map/new-template-dialog/new-template-dialog.component';
@Component({ @Component({
selector: 'app-template', selector: 'app-template',
@ -127,6 +128,19 @@ export class TemplateComponent implements OnInit, OnDestroy {
}); });
} }
addNewTemplate() {
const dialogRef = this.dialog.open(NewTemplateDialogComponent, {
width: '1000px',
maxHeight: '700px',
autoFocus: false,
disableClose: true,
});
let instance = dialogRef.componentInstance;
instance.server = this.server;
instance.project = this.project;
}
getImageSourceForTemplate(template: Template) { getImageSourceForTemplate(template: Template) {
return `${this.server.protocol}//${this.server.host}:${this.server.port}/v2/symbols/${template.symbol}/raw`; return `${this.server.protocol}//${this.server.host}:${this.server.port}/v2/symbols/${template.symbol}/raw`;
} }
@ -134,4 +148,4 @@ export class TemplateComponent implements OnInit, OnDestroy {
ngOnDestroy() { ngOnDestroy() {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
} }

View File

@ -61,7 +61,7 @@
{{ node.name }} {{ node.name }}
</div> </div>
<div *ngIf="node.console != null && node.console != undefined && node.console_type != 'none'"> <div *ngIf="node.console != null && node.console != undefined && node.console_type != 'none'">
{{ node.console_type }} {{ node.console_host }}:{{ node.console }} {{ node.console_type }}://{{ node.console_host }}:{{ node.console }}
</div> </div>
<div *ngIf="node.console === null || node.console === undefined || node.console_type === 'none'"> <div *ngIf="node.console === null || node.console === undefined || node.console_type === 'none'">
none none

View File

@ -78,7 +78,28 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
this.computes = computes; this.computes = computes;
}); });
this.style = { top: '60px', right: '0px', width: '320px', height: '400px' }; this.revertPosition();
}
revertPosition(){
let leftPosition = localStorage.getItem('leftPosition');
let rightPosition = localStorage.getItem('rightPosition');
let topPosition = localStorage.getItem('topPosition');
let widthOfWidget = localStorage.getItem('widthOfWidget');
let heightOfWidget = localStorage.getItem('heightOfWidget');
if (!topPosition) {
this.style = { top: '60px', right: '0px', width: '320px', height: '400px' };
} else {
this.style = {
position: 'fixed',
left: `${+leftPosition}px`,
right: `${+rightPosition}px`,
top: `${+topPosition}px`,
width: `${+widthOfWidget}px`,
height: `${+heightOfWidget}px`,
};
}
} }
toggleDragging(value: boolean) { toggleDragging(value: boolean) {
@ -101,6 +122,12 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
width: `${width}px`, width: `${width}px`,
height: `${height}px`, height: `${height}px`,
}; };
localStorage.setItem('leftPosition', left.toString());
localStorage.setItem('topPosition', top.toString());
localStorage.setItem('widthOfWidget', width.toString());
localStorage.setItem('heightOfWidget', height.toString());
} else { } else {
let right: number = Number(this.style['right'].split('px')[0]) - x; let right: number = Number(this.style['right'].split('px')[0]) - x;
this.style = { this.style = {
@ -110,6 +137,11 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
width: `${width}px`, width: `${width}px`,
height: `${height}px`, height: `${height}px`,
}; };
localStorage.setItem('rightPosition', right.toString());
localStorage.setItem('topPosition', top.toString());
localStorage.setItem('widthOfWidget', width.toString());
localStorage.setItem('heightOfWidget', height.toString());
} }
} }
@ -140,6 +172,7 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
toggleTopologyVisibility(value: boolean) { toggleTopologyVisibility(value: boolean) {
this.isTopologyVisible = value; this.isTopologyVisible = value;
this.revertPosition();
} }
compareAsc(first: Node, second: Node) { compareAsc(first: Node, second: Node) {

View File

@ -39,4 +39,4 @@
<app-progress></app-progress> <app-progress></app-progress>
<footer class="footer mat-app-background">GNS3 Web UI &copy; 2020 - v{{ uiVersion }}</footer> <footer class="footer mat-app-background">GNS3 Web-UI &copy;2018-{{ currentYear }} v{{ uiVersion }}</footer>

View File

@ -17,6 +17,7 @@ import { version } from './../../version';
export class DefaultLayoutComponent implements OnInit, OnDestroy { export class DefaultLayoutComponent implements OnInit, OnDestroy {
public isInstalledSoftwareAvailable = false; public isInstalledSoftwareAvailable = false;
public uiVersion = version; public uiVersion = version;
public currentYear = new Date().getFullYear();
serverStatusSubscription: Subscription; serverStatusSubscription: Subscription;
shouldStopServersOnClosing = true; shouldStopServersOnClosing = true;

View File

@ -55,6 +55,9 @@ export interface Iou {
export interface Images { export interface Images {
hda_disk_image: string; hda_disk_image: string;
hdb_disk_image: string; hdb_disk_image: string;
hdc_disk_image: string;
hdd_disk_image: string;
cdrom_image: string;
} }
export interface Version { export interface Version {
@ -74,11 +77,13 @@ export interface Appliance {
maintainer_email: string; maintainer_email: string;
name: string; name: string;
port_name_format: string; port_name_format: string;
port_segment_size: number;
product_name: string; product_name: string;
product_url: string; product_url: string;
registry_version: number; registry_version: number;
status: string; status: string;
symbol: string; symbol: string;
default_name_format: string;
usage: string; usage: string;
vendor_name: string; vendor_name: string;
vendor_url: string; vendor_url: string;

View File

@ -43,4 +43,7 @@ export class QemuTemplate {
template_id: string; template_id: string;
template_type: string; template_type: string;
usage: string; usage: string;
replicate_network_connection_state: boolean;
tpm: boolean;
uefi: boolean;
} }

View File

@ -19,7 +19,7 @@ export class DockerConfigurationService {
} }
getConsoleResolutions() { getConsoleResolutions() {
let consoleResolutions = ['1920x1080', '1366x768', '1280x1024', '1280x800', '1024x768', '800x600', '640x480']; let consoleResolutions = ['2560x1440', '1920x1080', '1680x1050', '1440x900', '1366x768', '1280x1024', '1280x800', '1024x768', '800x600', '640x480'];
return consoleResolutions; return consoleResolutions;
} }

View File

@ -149,7 +149,7 @@ describe('DrawingService', () => {
drawing.z = 1; drawing.z = 1;
drawing.rotation = 0; drawing.rotation = 0;
drawing.svg = drawing.svg =
'<svg height="100" width="200"><rect fill="#ffffff" fill-opacity="1.0" height="100" stroke="#000000" stroke-width="2" width="200" /></svg>'; '<svg height="100" width="200"><rect fill="#ffffff" fill-opacity="1.0" height="100" stroke="#000000" stroke-width="2" width="200" rx="0" ry="0" /></svg>';
service.duplicate(server, drawing.project_id, drawing).subscribe(); service.duplicate(server, drawing.project_id, drawing).subscribe();

View File

@ -1,14 +1,17 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { SettingsService } from './settings.service';
declare var gtag: Function; declare var gtag: Function;
@Injectable() @Injectable()
export class GoogleAnalyticsService { export class GoogleAnalyticsService {
constructor(router: Router) { private settingsService: SettingsService;
constructor(router: Router, settingsService: SettingsService) {
if (!environment.production) return; if (!environment.production) return;
router.events.subscribe((event) => { router.events.subscribe((event) => {
if (event instanceof NavigationEnd) { if (settingsService.getStatisticsSettings() && event instanceof NavigationEnd) {
gtag('set', 'page', event.url); gtag('set', 'page', event.url);
gtag('send', 'pageview'); gtag('send', 'pageview');
} }

View File

@ -63,9 +63,6 @@ export class LinkService {
} }
updateLink(server: Server, link: Link) { updateLink(server: Server, link: Link) {
link.x = Math.round(link.x);
link.y = Math.round(link.y);
return this.httpServer.put<Link>(server, `/projects/${link.project_id}/links/${link.link_id}`, link); return this.httpServer.put<Link>(server, `/projects/${link.project_id}/links/${link.link_id}`, link);
} }

View File

@ -78,13 +78,15 @@ export class NodeConsoleService {
let nodesToStart = 'Please start the following nodes if you want to open consoles for them: '; let nodesToStart = 'Please start the following nodes if you want to open consoles for them: ';
let nodesToStartCounter = 0; let nodesToStartCounter = 0;
nodes.forEach((n) => { nodes.forEach((n) => {
if (n.status === 'started') { if (n.console_type !== "none") {
this.mapSettingsService.logConsoleSubject.next(true); if (n.status === 'started') {
// this timeout is required due to xterm.js implementation this.mapSettingsService.logConsoleSubject.next(true);
setTimeout(() => { this.openConsoleForNode(n); }, 500); // this timeout is required due to xterm.js implementation
} else { setTimeout(() => { this.openConsoleForNode(n); }, 500);
nodesToStartCounter++; } else {
nodesToStart += n.name + ' ' nodesToStartCounter++;
nodesToStart += n.name + ' '
}
} }
}); });
if (nodesToStartCounter > 0) { if (nodesToStartCounter > 0) {
@ -93,16 +95,19 @@ export class NodeConsoleService {
} }
openConsolesForAllNodesInNewTabs(nodes: Node[]) { openConsolesForAllNodesInNewTabs(nodes: Node[]) {
let nodesToStart = 'Please start the following nodes if you want to open consoles for them: '; let nodesToStart = 'Please start the following nodes if you want to open consoles in tabs for them: ';
let nodesToStartCounter = 0; let nodesToStartCounter = 0;
nodes.forEach((n) => { nodes.forEach((n) => {
if (n.status === 'started') { // opening a console in tab is only supported for telnet type
let url = this.router.url.split('/'); if (n.console_type === "telnet") {
let urlString = `/static/web-ui/${url[1]}/${url[2]}/${url[3]}/${url[4]}/nodes/${n.node_id}`; if (n.status === 'started') {
window.open(urlString); let url = this.router.url.split('/');
} else { let urlString = `/static/web-ui/${url[1]}/${url[2]}/${url[3]}/${url[4]}/nodes/${n.node_id}`;
nodesToStartCounter++; window.open(urlString);
nodesToStart += n.name + ' ' } else {
nodesToStartCounter++;
nodesToStart += n.name + ' '
}
} }
}); });
if (nodesToStartCounter > 0) { if (nodesToStartCounter > 0) {

View File

@ -2,14 +2,17 @@ import { Injectable } from '@angular/core';
import { Link } from '../models/link'; import { Link } from '../models/link';
import { Project } from '../models/project'; import { Project } from '../models/project';
import { Server } from '../models/server'; import { Server } from '../models/server';
import { ProtocolHandlerService } from './protocol-handler.service';
@Injectable() @Injectable()
export class PacketCaptureService { export class PacketCaptureService {
constructor() {}
constructor(private protocolHandlerService: ProtocolHandlerService) {}
startCapture(server: Server, project: Project, link: Link, name: string) { startCapture(server: Server, project: Project, link: Link, name: string) {
location.assign(
`gns3+pcap://${server.host}:${server.port}?project_id=${project.project_id}&link_id=${link.link_id}&name=${name}` const uri = `gns3+pcap://${server.host}:${server.port}?protocol=${server.protocol.slice(0, -1)}&project_id=${project.project_id}&link_id=${link.link_id}&project=${project.name}&name=${name}`;
); this.protocolHandlerService.open(uri);
} }
} }

View File

@ -0,0 +1,48 @@
import { Injectable } from '@angular/core';
import { DeviceDetectorService } from 'ngx-device-detector';
import { ToasterService } from './toaster.service';
@Injectable()
export class ProtocolHandlerService {
constructor(private toasterService: ToasterService, private deviceService: DeviceDetectorService) {}
createHiddenIframe(target: Element, uri: string) {
const iframe = document.createElement("iframe");
iframe.src = uri;
iframe.id = "hiddenIframe";
iframe.style.display = "none";
target.appendChild(iframe);
return iframe;
}
openUriUsingFirefox(uri: string) {
var iframe = (document.querySelector("#hiddenIframe") as HTMLIFrameElement);
if (!iframe) {
iframe = this.createHiddenIframe(document.body, "about:blank");
}
try {
iframe.contentWindow.location.href = uri;
} catch (e) {
if (e.name === "NS_ERROR_UNKNOWN_PROTOCOL") {
this.toasterService.error('Protocol handler does not exist');
}
}
}
open(uri: string) {
const device = this.deviceService.getDeviceInfo();
console.log("Launching external protocol handler with " + device.browser + ": " + uri)
if (device.browser === "Firefox") {
// Use a hidden iframe otherwise Firefox will disconnect
// from the GNS3 controller websocket if we use location.assign()
this.openUriUsingFirefox(uri);
} else {
location.assign(uri);
}
}
}

View File

@ -12,53 +12,31 @@ export class QemuConfigurationService {
getNetworkTypes() { getNetworkTypes() {
// needs extending of custom adapter component // needs extending of custom adapter component
// let networkTypes = [["e1000", "Intel Gigabit Ethernet"],
// ["i82550", "Intel i82550 Ethernet"],
// ["i82551", "Intel i82551 Ethernet"],
// ["i82557a", "Intel i82557A Ethernet"],
// ["i82557b", "Intel i82557B Ethernet"],
// ["i82557c", "Intel i82557C Ethernet"],
// ["i82558a", "Intel i82558A Ethernet"],
// ["i82558b", "Intel i82558B Ethernet"],
// ["i82559a", "Intel i82559A Ethernet"],
// ["i82559b", "Intel i82559B Ethernet"],
// ["i82559c", "Intel i82559C Ethernet"],
// ["i82559er", "Intel i82559ER Ethernet"],
// ["i82562", "Intel i82562 Ethernet"],
// ["i82801", "Intel i82801 Ethernet"],
// ["ne2k_pci", "NE2000 Ethernet"],
// ["pcnet", "AMD PCNet Ethernet"],
// ["rtl8139", "Realtek 8139 Ethernet"],
// ["virtio", "Legacy paravirtualized Network I/O"],
// ["virtio-net-pci", "Paravirtualized Network I/O"],
// ["vmxnet3", "VMWare Paravirtualized Ethernet v3"]];
let networkTypes = [ let networkTypes = [
'e1000', { value: 'e1000', name: 'Intel Gigabit Ethernet' },
'e1000-82544gc', { value: 'e1000-82544gc', name: 'Intel 82544GC Gigabit Ethernet' },
'e1000-82545em', { value: 'e1000-82545em', name: 'Intel 82545EM Gigabit Ethernet' },
'e1000e', { value: 'e1000e', name: 'Intel PCIe Gigabit Ethernet' },
'rocker', { value: 'i82550', name: 'Intel i82550 Ethernet' },
'Intel Gigabit Ethernet', { value: 'i82551', name: 'Intel i82551 Ethernet' },
'i82550', { value: 'i82557a', name: 'Intel i82557A Ethernet' },
'i82551', { value: 'i82557b', name: 'Intel i82557B Ethernet' },
'i82557a', { value: 'i82557c', name: 'Intel i82557C Ethernet' },
'i82557b', { value: 'i82558a', name: 'Intel i82558A Ethernet' },
'i82557c', { value: 'i82558b', name: 'Intel i82558B Ethernet' },
'i82558a', { value: 'i82559a', name: 'Intel i82559A Ethernet' },
'i82558b', { value: 'i82559b', name: 'Intel i82559B Ethernet' },
'i82559a', { value: 'i82559c', name: 'Intel i82559C Ethernet' },
'i82559b', { value: 'i82559er', name: 'Intel i82559ER Ethernet' },
'i82559c', { value: 'i82562', name: 'Intel i82562 Ethernet' },
'i82559er', { value: 'i82801', name: 'Intel i82801 Ethernet' },
'i82562', { value: 'igb', name: 'Intel 82576 Gigabit Ethernet' },
'i82801', { value: 'ne2k_pci', name: 'NE2000 Ethernet' },
'ne2k_pci', { value: 'pcnet', name: 'AMD PCNet Ethernet' },
'pcnet', { value: 'rocker', name: 'Rocker L2 switch device' },
'rtl8139', { value: 'rtl8139', name: 'Realtek 8139 Ethernet' },
'virtio', { value: 'virtio-net-pci', name: 'Paravirtualized Network I/O' },
'virtio-net-pci', { value: 'vmxnet3', name: 'VMWare Paravirtualized Ethernet v3' },
'vmxnet3',
]; ];
return networkTypes; return networkTypes;

View File

@ -78,6 +78,9 @@ describe('QemuService', () => {
template_id: '1', template_id: '1',
template_type: 'qemu', template_type: 'qemu',
usage: '', usage: '',
replicate_network_connection_state: true,
tpm: false,
uefi: false,
}; };
service.saveTemplate(server, template).subscribe(); service.saveTemplate(server, template).subscribe();
@ -131,6 +134,9 @@ describe('QemuService', () => {
template_id: '', template_id: '',
template_type: 'qemu', template_type: 'qemu',
usage: '', usage: '',
replicate_network_connection_state: true,
tpm: false,
uefi: false,
}; };
service.addTemplate(server, template).subscribe(); service.addTemplate(server, template).subscribe();

View File

@ -4,6 +4,7 @@ import { BehaviorSubject } from 'rxjs';
export interface Settings { export interface Settings {
crash_reports: boolean; crash_reports: boolean;
console_command: string; console_command: string;
anonymous_statistics: boolean;
} }
@Injectable({ @Injectable({
@ -13,10 +14,12 @@ export class SettingsService {
private settings: Settings = { private settings: Settings = {
crash_reports: true, crash_reports: true,
console_command: undefined, console_command: undefined,
anonymous_statistics: true
}; };
private readonly reportsSettings: string = 'crash_reports'; private readonly reportsSettings: string = 'crash_reports';
private readonly consoleSettings: string = 'console_command'; private readonly consoleSettings: string = 'console_command';
private readonly statisticsSettings: string = 'statistics_command';
constructor() { constructor() {
if (this.getItem(this.reportsSettings)) if (this.getItem(this.reportsSettings))
@ -24,6 +27,9 @@ export class SettingsService {
if (this.getItem(this.consoleSettings)) if (this.getItem(this.consoleSettings))
this.settings.console_command = this.getItem(this.consoleSettings); this.settings.console_command = this.getItem(this.consoleSettings);
if (this.getItem(this.statisticsSettings))
this.settings.anonymous_statistics = this.getItem(this.statisticsSettings) === 'true' ? true : false;
} }
setReportsSettings(value: boolean) { setReportsSettings(value: boolean) {
@ -36,10 +42,24 @@ export class SettingsService {
} }
} }
setStatisticsSettings(value: boolean) {
this.settings.anonymous_statistics = value;
this.removeItem(this.statisticsSettings);
if (value) {
this.setItem(this.statisticsSettings, 'true');
} else {
this.setItem(this.statisticsSettings, 'false');
}
}
getReportsSettings() { getReportsSettings() {
return this.getItem(this.reportsSettings) === 'true' ? true : false; return this.getItem(this.reportsSettings) === 'true' ? true : false;
} }
getStatisticsSettings() {
return this.getItem(this.statisticsSettings) === 'true' ? true : false;
}
setConsoleSettings(value: string) { setConsoleSettings(value: string) {
this.settings.console_command = value; this.settings.console_command = value;
this.removeItem(this.consoleSettings); this.removeItem(this.consoleSettings);
@ -70,5 +90,6 @@ export class SettingsService {
this.settings = settings; this.settings = settings;
this.setConsoleSettings(settings.console_command); this.setConsoleSettings(settings.console_command);
this.setReportsSettings(settings.crash_reports); this.setReportsSettings(settings.crash_reports);
this.setStatisticsSettings(settings.anonymous_statistics);
} }
} }

View File

@ -75,6 +75,9 @@ export class TemplateMocksService {
template_id: '', template_id: '',
template_type: 'qemu', template_type: 'qemu',
usage: '', usage: '',
replicate_network_connection_state: true,
tpm: false,
uefi: false,
}; };
return of(template); return of(template);

View File

@ -38,7 +38,7 @@
<body class="mat-app-background" oncontextmenu="return false;"> <body class="mat-app-background" oncontextmenu="return false;">
<app-root></app-root> <app-root></app-root>
<!-- Global site tag (gtag.js) - Google Analytics --> <!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-5D6FZL9923"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=G-0BT7QQV1W1"></script>
<script> <script>
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag() { function gtag() {
@ -46,7 +46,7 @@
} }
gtag('js', new Date()); gtag('js', new Date());
gtag('config', 'G-5D6FZL9923'); gtag('config', 'G-0BT7QQV1W1');
</script> </script>
</body> </body>
</html> </html>

View File

@ -5,7 +5,9 @@ import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@ang
declare const require: any; declare const require: any;
// First, initialize the Angular testing environment. // First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
});
// Then we find all the tests. // Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/); const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules. // And load the modules.

View File

@ -1,4 +1,4 @@
@import '~@angular/material/theming'; @import '@angular/material/theming';
@import '~material-design-icons/iconfont/material-icons.css'; @import '~material-design-icons/iconfont/material-icons.css';
@import '~typeface-roboto/index.css'; @import '~typeface-roboto/index.css';
@include mat-core(); @include mat-core();

7247
yarn.lock

File diff suppressed because it is too large Load Diff