Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cc12195a3 | ||
|
|
66d4d39d5b | ||
|
|
a5dbe66fc1 | ||
|
|
4c8be45e28 | ||
|
|
6049a2b834 | ||
|
|
831c014f97 | ||
|
|
5c15a7a64a | ||
|
|
e43992b278 | ||
|
|
5963d2bac5 | ||
|
|
45816a2bcc | ||
|
|
60a5a2aa40 | ||
|
|
04b9b59e14 | ||
|
|
4ff4b52202 | ||
|
|
73c426844a | ||
|
|
25ce236a77 | ||
|
|
9b20175545 |
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist_electron
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
.github
|
||||||
|
.git
|
||||||
|
docs
|
||||||
|
docker
|
||||||
7
.github/workflows/bump-and-release.yaml
vendored
@@ -10,9 +10,8 @@ jobs:
|
|||||||
if: github.event.base_ref == 'refs/heads/master'
|
if: github.event.base_ref == 'refs/heads/master'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
- uses: undergroundwires/bump-everywhere@master
|
||||||
uses: undergroundwires/bump-everywhere@master
|
|
||||||
with:
|
with:
|
||||||
user: undergroundwires-bot
|
user: undergroundwires-bot
|
||||||
release-token: ${{secrets.BUMP_GITHUB_PAT}} # Does not trigger release pipeline if we use default token: https://github.community/t5/GitHub-Actions/Github-Action-trigger-on-release-not-working-if-releases-was/td-p/34559
|
release-token: ${{ secrets.BUMP_GITHUB_PAT }} # Does not trigger release pipeline if we use default token: https://github.community/t5/GitHub-Actions/Github-Action-trigger-on-release-not-working-if-releases-was/td-p/34559
|
||||||
# GitHub does not inject secrets if pipeline runs from fork or a fork is merged to main repo.
|
# GitHub does not inject secrets if pipeline runs from fork or a fork is merged to main repo.
|
||||||
|
|||||||
32
.github/workflows/deploy-desktop.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Deploy desktop
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created] # will be triggered when a NON-draft release is created and published.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish-desktop-app:
|
||||||
|
name: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos, ubuntu, windows]
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: master # otherwise it defaults to the version tag missing bump commit
|
||||||
|
fetch-depth: 0 # fetch all history
|
||||||
|
- name: Checkout to bump commit
|
||||||
|
run: git checkout "$(git rev-list "${{ github.event.release.tag_name }}"..master | tail -1)"
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '14.x'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: Run tests
|
||||||
|
run: npm run test:unit
|
||||||
|
- name: Publish desktop app
|
||||||
|
run: npm run electron:build -- -p always # https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#upload-release-to-github
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
name: Build & deploy
|
name: Deploy site
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created] # will be triggered when a NON-draft release is created and published.
|
types: [created] # will be triggered when a NON-draft release is created and published.
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-deploy:
|
aws-deploy: # see: https://github.com/undergroundwires/aws-static-site-with-cd
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
node-version: '14.x'
|
node-version: '14.x'
|
||||||
-
|
-
|
||||||
name: "App: Install dependencies"
|
name: "App: Install dependencies"
|
||||||
run: npm install
|
run: npm ci
|
||||||
working-directory: site
|
working-directory: site
|
||||||
-
|
-
|
||||||
name: "App: Run tests"
|
name: "App: Run tests"
|
||||||
39
.github/workflows/quality-checks.yaml
vendored
@@ -1,37 +1,26 @@
|
|||||||
name: Quality checks
|
name: Quality checks
|
||||||
|
|
||||||
on:
|
on: push
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
lint-command:
|
||||||
|
- npm run lint:vue
|
||||||
|
- npm run lint:yaml
|
||||||
|
- npm run lint:md
|
||||||
|
- npm run lint:md:relative-urls
|
||||||
|
- npm run lint:md:consistency
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
-
|
- name: Setup node
|
||||||
name: Setup node
|
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 14.x
|
node-version: 14.x
|
||||||
-
|
- name: Install dependencies
|
||||||
name: Install dependencies
|
|
||||||
run: npm ci
|
run: npm ci
|
||||||
-
|
- name: Lint
|
||||||
name: Lint vue
|
run: ${{ matrix.lint-command }}
|
||||||
run: npm run lint:vue
|
|
||||||
-
|
|
||||||
name: Lint yaml
|
|
||||||
run: npm run lint:yaml
|
|
||||||
-
|
|
||||||
name: 'Validate md: Relative URLs'
|
|
||||||
run: npm run lint:md:relative-urls
|
|
||||||
-
|
|
||||||
name: 'Validate md: Enforce standards'
|
|
||||||
run: npm run lint:md
|
|
||||||
-
|
|
||||||
name: 'Validate md: Ensure consistency'
|
|
||||||
run: npm run lint:md:consistency
|
|
||||||
|
|||||||
4
.github/workflows/security-checks.yaml
vendored
@@ -1,9 +1,7 @@
|
|||||||
name: Security checks
|
name: Security checks
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
push:
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 0'
|
- cron: '0 0 * * 0'
|
||||||
|
|
||||||
|
|||||||
5
.github/workflows/test.yaml
vendored
@@ -1,9 +1,6 @@
|
|||||||
name: Test
|
name: Test
|
||||||
|
|
||||||
on:
|
on: push
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-tests:
|
run-tests:
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -2,3 +2,5 @@ node_modules
|
|||||||
/dist
|
/dist
|
||||||
.vs
|
.vs
|
||||||
.vscode
|
.vscode
|
||||||
|
#Electron-builder output
|
||||||
|
/dist_electron
|
||||||
25
CHANGELOG.md
@@ -1,8 +1,31 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.6.0 (2020-07-26)
|
||||||
|
|
||||||
|
* fixed dead links in documentation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/25ce236a7737decaf2eb9b8c29a4c4f34d43f770)
|
||||||
|
* runs tests on each push on the repository | [commit](https://github.com/undergroundwires/privacy.sexy/commit/73c426844a0330718a9ab7de12b61ca05e853323)
|
||||||
|
* code area now shows "how" before "why" | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4ff4b52202b1c5dbfe2b80580bbe7d93132ab05c)
|
||||||
|
* support for desktop versions #20 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/5a27f9d86c96be88bb52aa69a84a7e057ac072be)
|
||||||
|
* reworked on footer & removed github icon | [commit](https://github.com/undergroundwires/privacy.sexy/commit/8ab062454db1d8504fb2df4fbb39a7002bafeb09)
|
||||||
|
* updated dependencies to latest | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c335ab33ff612a19af1278bdcc88a14779e8bb91)
|
||||||
|
* updated documentation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/81dfbbef7a8786751fa7693010c80561c5ac6bce)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.5.0...0.6.0)
|
||||||
|
|
||||||
|
## 0.5.0 (2020-07-19)
|
||||||
|
|
||||||
|
* added ability to revert (#21) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/9c063d59defa6297c64f50b49403e8bd10620de9)
|
||||||
|
* search placeholder shows total scripts | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1d5225de07186f853f4cf7aedd4998f5d00c107a)
|
||||||
|
* do not collapse card when on "Search" and "Select" | [commit](https://github.com/undergroundwires/privacy.sexy/commit/dd7e1416b4df54bf71b719d4654db88769dc0994)
|
||||||
|
* opening a card scrolls to its content div | [commit](https://github.com/undergroundwires/privacy.sexy/commit/31d2067f076c3159483baec49975617dddbd158d)
|
||||||
|
* all cards in same line now have same height | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a9f9e9044385d9aed3b5551fc6c6823e813fd1e5)
|
||||||
|
* patched loadash vulnerability (#18) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/92a7118d1c5013312772e075b9ee5a79c93710b8)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.10...0.5.0)
|
||||||
|
|
||||||
## 0.4.10 (2020-07-15)
|
## 0.4.10 (2020-07-15)
|
||||||
|
|
||||||
* fixed script validation errors | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a34ae139d7fc1ba05b8ab9eb962da4ca0231ed5c)
|
* fixed script errors & added tests | [commit](https://github.com/undergroundwires/privacy.sexy/commit/9e722ddfb3825fb29d6298025baaaa033120d017)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.9...0.4.10)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.9...0.4.10)
|
||||||
|
|
||||||
|
|||||||
@@ -20,25 +20,24 @@
|
|||||||
6. Issue that pull request!
|
6. Issue that pull request!
|
||||||
- 🙏 DO
|
- 🙏 DO
|
||||||
- Document your changes in the pull request
|
- Document your changes in the pull request
|
||||||
- 💡 Check [developer notes](./docs/developer-notes.md) if you need help
|
|
||||||
- ❗ DON'T
|
- ❗ DON'T
|
||||||
- Do not update the versions, current version is only [set by the maintainer](./docs/gitops.png) and updated automatically by [bump-everywhere](https://github.com/undergroundwires/bump-everywhere)
|
- Do not update the versions, current version is only [set by the maintainer](./img/gitops.png) and updated automatically by [bump-everywhere](https://github.com/undergroundwires/bump-everywhere)
|
||||||
|
|
||||||
## Guidelines
|
## Guidelines
|
||||||
|
|
||||||
### Extend scripts
|
### Extend scripts
|
||||||
|
|
||||||
- Create a [pull request](./../CONTRIBUTING.md#Pull+Request+Process) for [application.yaml](./../src/application/application.yaml)
|
- Create a [pull request](#Pull-Request-Process) for [application.yaml](./src/application/application.yaml)
|
||||||
- 🙏 For any new script, try to add `revertCode` that'll revert the changes caused by the script.
|
- 🙏 For any new script, try to add `revertCode` that'll revert the changes caused by the script.
|
||||||
- See [typings](./../src/application/application.yaml.d.ts) for documentation as code.
|
- See [typings](./src/application/application.yaml.d.ts) for documentation as code.
|
||||||
|
|
||||||
### Handle the state in presentation layer
|
### Handle the state in presentation layer
|
||||||
|
|
||||||
- There are two types of components:
|
- There are two types of components:
|
||||||
- **Stateless**, extends `Vue`
|
- **Stateless**, extends `Vue`
|
||||||
- **Stateful**, extends [`StatefulVue`](./src/presentation/StatefulVue.ts)
|
- **Stateful**, extends [`StatefulVue`](./src/presentation/StatefulVue.ts)
|
||||||
- The source of truth for the state lies in [application layer](./src/application/state) and must be updated from the views if they're mutating the state
|
- The source of truth for the state lies in application layer (`./src/application/`) and must be updated from the views if they're mutating the state
|
||||||
- They mutate or/and reacts to changes in [application state](./src/application/state).
|
- They mutate or/and reacts to changes in [application state](src/application/State/ApplicationState.ts).
|
||||||
- You can react by getting the state and listening to it and update the view accordingly in [`mounted()`](https://vuejs.org/v2/api/#mounted) method.
|
- You can react by getting the state and listening to it and update the view accordingly in [`mounted()`](https://vuejs.org/v2/api/#mounted) method.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
33
README.md
@@ -1,6 +1,6 @@
|
|||||||
# privacy.sexy
|
# privacy.sexy
|
||||||
|
|
||||||
> Web tool to enforce privacy & security best-practices on Windows, because privacy is sexy 🍑🍆
|
> Enforce privacy & security best-practices on Windows, because privacy is sexy 🍑🍆
|
||||||
|
|
||||||
[](./CONTRIBUTING.md)
|
[](./CONTRIBUTING.md)
|
||||||
[](https://lgtm.com/projects/g/undergroundwires/privacy.sexy/context:javascript)
|
[](https://lgtm.com/projects/g/undergroundwires/privacy.sexy/context:javascript)
|
||||||
@@ -12,16 +12,23 @@
|
|||||||
[](https://github.com/undergroundwires/privacy.sexy/actions)
|
[](https://github.com/undergroundwires/privacy.sexy/actions)
|
||||||
[](https://github.com/undergroundwires/bump-everywhere)
|
[](https://github.com/undergroundwires/bump-everywhere)
|
||||||
|
|
||||||
[https://privacy.sexy](https://privacy.sexy)
|
## Get started
|
||||||
|
|
||||||
|
- Online version: [https://privacy.sexy](https://privacy.sexy)
|
||||||
|
- or download latest desktop version for [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.6.0/privacy.sexy-Setup-0.6.0.exe), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.6.0/privacy.sexy-0.6.0.AppImage), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.6.0/privacy.sexy-0.6.0.dmg)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
- You don't need to run any compiled software that has access to your system, just run the generated scripts.
|
- You don't need to run any compiled software that has access to your system, just run the generated scripts.
|
||||||
- It's open source, both application & infrastructure is 100% transparent
|
|
||||||
- Fully automated C/CD pipeline to AWS for provisioning serverless infrastructure using GitHub actions.
|
|
||||||
- Have full visibility into what the tweaks do as you enable them.
|
- Have full visibility into what the tweaks do as you enable them.
|
||||||
- Ability to revert applied scripts
|
- Ability to revert applied scripts
|
||||||
- Easily extendable
|
- Easily extendable
|
||||||
|
- Everything is open-sourced including both application and infrastructure
|
||||||
|
- Fully automated CI/CD pipeline using GitHub actions
|
||||||
|
- to AWS for provisioning serverless infrastructure
|
||||||
|
- for building and sharing the desktop applications
|
||||||
|
|
||||||
## Extend scripts
|
## Extend scripts
|
||||||
|
|
||||||
@@ -34,34 +41,38 @@
|
|||||||
- Testing
|
- Testing
|
||||||
- Run unit tests: `npm run test:unit`
|
- Run unit tests: `npm run test:unit`
|
||||||
- Lint: `npm run lint`
|
- Lint: `npm run lint`
|
||||||
|
- **Desktop app**
|
||||||
|
- Development: `npm run electron:serve`
|
||||||
|
- Production: `npm run electron:build` to build an executable
|
||||||
- **Webpage**
|
- **Webpage**
|
||||||
- Development: `npm run serve` to compile & hot-reload for development.
|
- Development: `npm run serve` to compile & hot-reload for development.
|
||||||
- Production: `npm run build` to prepare files for distribution.
|
- Production: `npm run build` to prepare files for distribution.
|
||||||
- Or run using Docker:
|
- Or run using Docker:
|
||||||
1. Build: `docker build -t undergroundwires/privacy.sexy:0.4.10 .`
|
1. Build: `docker build -t undergroundwires/privacy.sexy:0.6.0 .`
|
||||||
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy-0.4.10 undergroundwires/privacy.sexy:0.4.10`
|
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy-0.6.0 undergroundwires/privacy.sexy:0.6.0`
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Application
|
### Application
|
||||||
|
|
||||||
- Powered by **TypeScript** + **Vue.js** 💪
|
- Powered by **TypeScript**, **Vue.js** and **Electron** 💪
|
||||||
- and driven by **Domain-driven design**, **Event-driven architecture**, **Data-driven programming** concepts.
|
- and driven by **Domain-driven design**, **Event-driven architecture**, **Data-driven programming** concepts.
|
||||||
- Application uses highly decoupled models & services in different DDD layers.
|
- Application uses highly decoupled models & services in different DDD layers.
|
||||||
- **Domain layer** is where the application is modelled with validation logic.
|
- **Domain layer** is where the application is modelled with validation logic.
|
||||||
- **Presentation Layer**
|
- **Presentation Layer**
|
||||||
- Consists of Vue.js components & UI stuff.
|
- Consists of Vue.js components and other UI-related code.
|
||||||
|
- Desktop application is created using [Electron](https://www.electronjs.org/).
|
||||||
- Event driven as in components simply listens to events from the state and act accordingly.
|
- Event driven as in components simply listens to events from the state and act accordingly.
|
||||||
- **Application Layer**
|
- **Application Layer**
|
||||||
- Keeps the application state
|
- Keeps the application state
|
||||||
- The [state](src/application/State/ApplicationState.ts) is a mutable singleton & event producer.
|
- The [state](src/application/State/ApplicationState.ts) is a mutable singleton & event producer.
|
||||||
- The application is defined & controlled in a [single YAML file](src/application/application.yaml) (see [Data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming))
|
- The application is defined & controlled in a [single YAML file](src/application/application.yaml) (see [Data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming))
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### AWS Infrastructure
|
### AWS Infrastructure
|
||||||
|
|
||||||
[](https://github.com/undergroundwires/aws-static-site-with-cd)
|
[](https://github.com/undergroundwires/aws-static-site-with-cd)
|
||||||
|
|
||||||
- It uses infrastructure from the following repository: [aws-static-site-with-cd](https://github.com/undergroundwires/aws-static-site-with-cd)
|
- It uses infrastructure from the following repository: [aws-static-site-with-cd](https://github.com/undergroundwires/aws-static-site-with-cd)
|
||||||
- Runs on AWS 100% serverless and automatically provisioned using [GitHub Actions](.github/workflows/).
|
- Runs on AWS 100% serverless and automatically provisioned using [GitHub Actions](.github/workflows/).
|
||||||
@@ -73,4 +84,4 @@
|
|||||||
- Versioning, tagging, creation of `CHANGELOG.md` and releasing is automated using [bump-everywhere](https://github.com/undergroundwires/bump-everywhere) action
|
- Versioning, tagging, creation of `CHANGELOG.md` and releasing is automated using [bump-everywhere](https://github.com/undergroundwires/bump-everywhere) action
|
||||||
- Everything that's merged in the master goes directly to production.
|
- Everything that's merged in the master goes directly to production.
|
||||||
|
|
||||||
[](.github/workflows/)
|
[](.github/workflows/)
|
||||||
|
|||||||
BIN
docs/gitops.png
|
Before Width: | Height: | Size: 233 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
BIN
img/app.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
1
img/gitops.drawio
Normal file
BIN
img/gitops.png
Normal file
|
After Width: | Height: | Size: 483 KiB |
5184
package-lock.json
generated
69
package.json
@@ -1,53 +1,66 @@
|
|||||||
{
|
{
|
||||||
"name": "privacy.sexy",
|
"name": "privacy.sexy",
|
||||||
"version": "0.4.10",
|
"version": "0.6.0",
|
||||||
|
"author": "undergroundwires",
|
||||||
|
"description": "Enforce privacy & security best-practices on Windows, because privacy is sexy 🍑🍆",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service build",
|
||||||
"test:unit": "vue-cli-service test:unit",
|
"test:unit": "vue-cli-service test:unit",
|
||||||
"lint": "npm run lint:vue && npm run lint:yaml && npm run lint:md && npm run lint:md:relative-urls && npm run lint:md:consistency",
|
"lint": "npm run lint:vue && npm run lint:yaml && npm run lint:md && npm run lint:md:relative-urls && npm run lint:md:consistency",
|
||||||
|
"electron:build": "vue-cli-service electron:build",
|
||||||
|
"electron:serve": "vue-cli-service electron:serve",
|
||||||
|
"lint:md": "markdownlint **/*.md --ignore node_modules",
|
||||||
|
"lint:md:consistency": "remark . --frail --use remark-preset-lint-consistent",
|
||||||
|
"lint:md:relative-urls": "remark . --frail --use remark-validate-links",
|
||||||
"lint:vue": "vue-cli-service lint --no-fix",
|
"lint:vue": "vue-cli-service lint --no-fix",
|
||||||
"lint:yaml": "yamllint **/*.yaml --ignore=node_modules/**/*.yaml",
|
"lint:yaml": "yamllint **/*.yaml --ignore=node_modules/**/*.yaml",
|
||||||
"lint:md": "markdownlint **/*.md --ignore node_modules",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"lint:md:relative-urls": "remark . --frail --use remark-validate-links",
|
"postuninstall": "electron-builder install-app-deps"
|
||||||
"lint:md:consistency": "remark . --frail --use remark-preset-lint-consistent"
|
|
||||||
},
|
},
|
||||||
|
"main": "background.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.26",
|
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
||||||
"@fortawesome/free-brands-svg-icons": "^5.12.0",
|
"@fortawesome/free-brands-svg-icons": "^5.14.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "^5.12.0",
|
"@fortawesome/free-regular-svg-icons": "^5.14.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
||||||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
"@fortawesome/vue-fontawesome": "^0.1.10",
|
||||||
"ace-builds": "^1.4.7",
|
"ace-builds": "^1.4.12",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"inversify": "^5.0.1",
|
"inversify": "^5.0.1",
|
||||||
"liquor-tree": "^0.2.70",
|
"liquor-tree": "^0.2.70",
|
||||||
"v-tooltip": "^2.0.2",
|
"v-tooltip": "^2.0.2",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-class-component": "^7.1.0",
|
"vue-class-component": "^7.2.5",
|
||||||
"vue-js-modal": "^2.0.0-rc.3",
|
"vue-js-modal": "^2.0.0-rc.6",
|
||||||
"vue-property-decorator": "^8.3.0"
|
"vue-property-decorator": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/ace": "0.0.42",
|
"@types/ace": "0.0.43",
|
||||||
"@types/chai": "^4.2.7",
|
"@types/chai": "^4.2.12",
|
||||||
"@types/file-saver": "^2.0.1",
|
"@types/file-saver": "^2.0.1",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^8.0.0",
|
||||||
"@vue/cli-plugin-typescript": "^4.4.0",
|
"@types/node": "12.0.0",
|
||||||
"@vue/cli-plugin-unit-mocha": "^4.1.1",
|
"@vue/cli-plugin-typescript": "^4.4.6",
|
||||||
"@vue/cli-service": "^4.1.1",
|
"@vue/cli-plugin-unit-mocha": "^4.4.6",
|
||||||
"@vue/test-utils": "1.0.0-beta.30",
|
"@vue/cli-service": "^4.4.6",
|
||||||
|
"@vue/test-utils": "1.0.3",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
|
"electron": "^9.1.1",
|
||||||
|
"electron-devtools-installer": "^3.1.1",
|
||||||
|
"electron-log": "^4.2.2",
|
||||||
|
"electron-updater": "^4.3.4",
|
||||||
"js-yaml-loader": "^1.2.2",
|
"js-yaml-loader": "^1.2.2",
|
||||||
"markdownlint-cli": "^0.23.1",
|
"markdownlint-cli": "^0.23.2",
|
||||||
"remark-cli": "^8.0.0",
|
"remark-cli": "^8.0.1",
|
||||||
"remark-lint-no-dead-urls": "^1.0.2",
|
"remark-lint-no-dead-urls": "^1.1.0",
|
||||||
"remark-preset-lint-consistent": "^3.0.0",
|
"remark-preset-lint-consistent": "^3.0.1",
|
||||||
"remark-validate-links": "^10.0.0",
|
"remark-validate-links": "^10.0.2",
|
||||||
"sass": "^1.24.0",
|
"sass": "^1.26.10",
|
||||||
"sass-loader": "^8.0.0",
|
"sass-loader": "^9.0.2",
|
||||||
"typescript": "^3.7.4",
|
"typescript": "^3.9.7",
|
||||||
|
"vue-cli-plugin-electron-builder": "^2.0.0-rc.4",
|
||||||
"vue-template-compiler": "^2.6.11",
|
"vue-template-compiler": "^2.6.11",
|
||||||
"yaml-lint": "^1.2.4"
|
"yaml-lint": "^1.2.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
import { Component, Vue, Prop } from 'vue-property-decorator';
|
import { Component, Vue, Prop } from 'vue-property-decorator';
|
||||||
import { ApplicationState } from '@/application/State/ApplicationState';
|
import { ApplicationState } from '@/application/State/ApplicationState';
|
||||||
import TheHeader from '@/presentation/TheHeader.vue';
|
import TheHeader from '@/presentation/TheHeader.vue';
|
||||||
import TheFooter from '@/presentation/TheFooter.vue';
|
import TheFooter from '@/presentation/TheFooter/TheFooter.vue';
|
||||||
import TheCodeArea from '@/presentation/TheCodeArea.vue';
|
import TheCodeArea from '@/presentation/TheCodeArea.vue';
|
||||||
import TheCodeButtons from '@/presentation/TheCodeButtons.vue';
|
import TheCodeButtons from '@/presentation/TheCodeButtons.vue';
|
||||||
import TheSearchBar from '@/presentation/TheSearchBar.vue';
|
import TheSearchBar from '@/presentation/TheSearchBar.vue';
|
||||||
|
|||||||
54
src/application/Environment/BrowserOs/BrowserOsDetector.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { OperatingSystem } from '../OperatingSystem';
|
||||||
|
import { DetectorBuilder } from './DetectorBuilder';
|
||||||
|
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
||||||
|
|
||||||
|
export class BrowserOsDetector implements IBrowserOsDetector {
|
||||||
|
private readonly detectors = BrowserDetectors;
|
||||||
|
public detect(userAgent: string): OperatingSystem {
|
||||||
|
if (!userAgent) {
|
||||||
|
return OperatingSystem.Unknown;
|
||||||
|
}
|
||||||
|
for (const detector of this.detectors) {
|
||||||
|
const os = detector.detect(userAgent);
|
||||||
|
if (os !== OperatingSystem.Unknown) {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OperatingSystem.Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference: https://github.com/keithws/browser-report/blob/master/index.js#L304
|
||||||
|
const BrowserDetectors =
|
||||||
|
[
|
||||||
|
define(OperatingSystem.KaiOS, (b) =>
|
||||||
|
b.mustInclude('KAIOS')),
|
||||||
|
define(OperatingSystem.ChromeOS, (b) =>
|
||||||
|
b.mustInclude('CrOS')),
|
||||||
|
define(OperatingSystem.BlackBerryOS, (b) =>
|
||||||
|
b.mustInclude('BlackBerry')),
|
||||||
|
define(OperatingSystem.BlackBerryTabletOS, (b) =>
|
||||||
|
b.mustInclude('RIM Tablet OS')),
|
||||||
|
define(OperatingSystem.BlackBerry, (b) =>
|
||||||
|
b.mustInclude('BB10')),
|
||||||
|
define(OperatingSystem.Android, (b) =>
|
||||||
|
b.mustInclude('Android').mustNotInclude('Windows Phone')),
|
||||||
|
define(OperatingSystem.Android, (b) =>
|
||||||
|
b.mustInclude('Adr').mustNotInclude('Windows Phone')),
|
||||||
|
define(OperatingSystem.iOS, (b) =>
|
||||||
|
b.mustInclude('like Mac OS X')),
|
||||||
|
define(OperatingSystem.Linux, (b) =>
|
||||||
|
b.mustInclude('Linux').mustNotInclude('Android').mustNotInclude('Adr')),
|
||||||
|
define(OperatingSystem.Windows, (b) =>
|
||||||
|
b.mustInclude('Windows').mustNotInclude('Windows Phone')),
|
||||||
|
define(OperatingSystem.WindowsPhone, (b) =>
|
||||||
|
b.mustInclude('Windows Phone')),
|
||||||
|
define(OperatingSystem.macOS, (b) =>
|
||||||
|
b.mustInclude('OS X').mustNotInclude('Android').mustNotInclude('like Mac OS X')),
|
||||||
|
];
|
||||||
|
|
||||||
|
function define(os: OperatingSystem, applyRules: (builder: DetectorBuilder) => DetectorBuilder): IBrowserOsDetector {
|
||||||
|
const builder = new DetectorBuilder(os);
|
||||||
|
applyRules(builder);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
53
src/application/Environment/BrowserOs/DetectorBuilder.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
||||||
|
import { OperatingSystem } from '../OperatingSystem';
|
||||||
|
|
||||||
|
export class DetectorBuilder {
|
||||||
|
private readonly existingPartsInUserAgent = new Array<string>();
|
||||||
|
private readonly notExistingPartsInUserAgent = new Array<string>();
|
||||||
|
|
||||||
|
constructor(private readonly os: OperatingSystem) { }
|
||||||
|
|
||||||
|
public mustInclude(str: string): DetectorBuilder {
|
||||||
|
return this.add(str, this.existingPartsInUserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public mustNotInclude(str: string): DetectorBuilder {
|
||||||
|
return this.add(str, this.notExistingPartsInUserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public build(): IBrowserOsDetector {
|
||||||
|
if (!this.existingPartsInUserAgent.length) {
|
||||||
|
throw new Error('Must include at least a part');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
detect: (agent) => this.detect(agent),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private detect(userAgent: string): OperatingSystem {
|
||||||
|
if (!userAgent) {
|
||||||
|
throw new Error('User agent is null or undefined');
|
||||||
|
}
|
||||||
|
if (this.existingPartsInUserAgent.some((part) => !userAgent.includes(part))) {
|
||||||
|
return OperatingSystem.Unknown;
|
||||||
|
}
|
||||||
|
if (this.notExistingPartsInUserAgent.some((part) => userAgent.includes(part))) {
|
||||||
|
return OperatingSystem.Unknown;
|
||||||
|
}
|
||||||
|
return this.os;
|
||||||
|
}
|
||||||
|
|
||||||
|
private add(part: string, array: string[]): DetectorBuilder {
|
||||||
|
if (!part) {
|
||||||
|
throw new Error('part is empty or undefined');
|
||||||
|
}
|
||||||
|
if (this.existingPartsInUserAgent.includes(part)) {
|
||||||
|
throw new Error(`part ${part} is already included as existing part`);
|
||||||
|
}
|
||||||
|
if (this.notExistingPartsInUserAgent.includes(part)) {
|
||||||
|
throw new Error(`part ${part} is already included as not existing part`);
|
||||||
|
}
|
||||||
|
array.push(part);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { OperatingSystem } from '../OperatingSystem';
|
||||||
|
|
||||||
|
export interface IBrowserOsDetector {
|
||||||
|
detect(userAgent: string): OperatingSystem;
|
||||||
|
}
|
||||||
80
src/application/Environment/Environment.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { BrowserOsDetector } from './BrowserOs/BrowserOsDetector';
|
||||||
|
import { IBrowserOsDetector } from './BrowserOs/IBrowserOsDetector';
|
||||||
|
import { IEnvironment } from './IEnvironment';
|
||||||
|
import { OperatingSystem } from './OperatingSystem';
|
||||||
|
|
||||||
|
interface IEnvironmentVariables {
|
||||||
|
readonly window: Window & typeof globalThis;
|
||||||
|
readonly process: NodeJS.Process;
|
||||||
|
readonly navigator: Navigator;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Environment implements IEnvironment {
|
||||||
|
public static readonly CurrentEnvironment: IEnvironment = new Environment({
|
||||||
|
window,
|
||||||
|
process,
|
||||||
|
navigator,
|
||||||
|
});
|
||||||
|
public readonly isDesktop: boolean;
|
||||||
|
public readonly os: OperatingSystem;
|
||||||
|
protected constructor(
|
||||||
|
variables: IEnvironmentVariables,
|
||||||
|
browserOsDetector: IBrowserOsDetector = new BrowserOsDetector()) {
|
||||||
|
if (!variables) {
|
||||||
|
throw new Error('variables is null or empty');
|
||||||
|
}
|
||||||
|
this.isDesktop = isDesktop(variables);
|
||||||
|
this.os = this.isDesktop ?
|
||||||
|
getDesktopOsType(getProcessPlatform(variables))
|
||||||
|
: browserOsDetector.detect(getUserAgent(variables));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserAgent(variables: IEnvironmentVariables): string {
|
||||||
|
if (!variables.window || !variables.window.navigator) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return variables.window.navigator.userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProcessPlatform(variables: IEnvironmentVariables): string {
|
||||||
|
if (!variables.process || !variables.process.platform) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return variables.process.platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDesktopOsType(processPlatform: string): OperatingSystem {
|
||||||
|
// https://nodejs.org/api/process.html#process_process_platform
|
||||||
|
if (processPlatform === 'darwin') {
|
||||||
|
return OperatingSystem.macOS;
|
||||||
|
} else if (processPlatform === 'win32') {
|
||||||
|
return OperatingSystem.Windows;
|
||||||
|
} else if (processPlatform === 'linux') {
|
||||||
|
return OperatingSystem.Linux;
|
||||||
|
}
|
||||||
|
return OperatingSystem.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDesktop(variables: IEnvironmentVariables): boolean {
|
||||||
|
// More: https://github.com/electron/electron/issues/2288
|
||||||
|
// Renderer process
|
||||||
|
if (variables.window
|
||||||
|
&& variables.window.process
|
||||||
|
&& variables.window.process.type === 'renderer') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Main process
|
||||||
|
if (variables.process
|
||||||
|
&& variables.process.versions
|
||||||
|
&& Boolean(variables.process.versions.electron)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Detect the user agent when the `nodeIntegration` option is set to true
|
||||||
|
if (variables.navigator
|
||||||
|
&& variables.navigator.userAgent
|
||||||
|
&& variables.navigator.userAgent.includes('Electron')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
6
src/application/Environment/IEnvironment.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { OperatingSystem } from './OperatingSystem';
|
||||||
|
|
||||||
|
export interface IEnvironment {
|
||||||
|
isDesktop: boolean;
|
||||||
|
os: OperatingSystem;
|
||||||
|
}
|
||||||
14
src/application/Environment/OperatingSystem.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export enum OperatingSystem {
|
||||||
|
macOS,
|
||||||
|
Windows,
|
||||||
|
Linux,
|
||||||
|
KaiOS,
|
||||||
|
ChromeOS,
|
||||||
|
BlackBerryOS,
|
||||||
|
BlackBerry,
|
||||||
|
BlackBerryTabletOS,
|
||||||
|
Android,
|
||||||
|
iOS,
|
||||||
|
WindowsPhone,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
@@ -1,37 +1,46 @@
|
|||||||
import { YamlDocumentable } from 'js-yaml-loader!./application.yaml';
|
import { YamlDocumentable, DocumentationUrls } from 'js-yaml-loader!./application.yaml';
|
||||||
|
|
||||||
export function parseDocUrls(documentable: YamlDocumentable): ReadonlyArray<string> {
|
export function parseDocUrls(documentable: YamlDocumentable): ReadonlyArray<string> {
|
||||||
if (!documentable) {
|
if (!documentable) {
|
||||||
throw new Error('documentable is null or undefined');
|
throw new Error('documentable is null or undefined');
|
||||||
}
|
}
|
||||||
const docs = documentable.docs;
|
const docs = documentable.docs;
|
||||||
if (!docs) {
|
if (!docs || !docs.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const result = new DocumentationUrls();
|
let result = new DocumentationUrlContainer();
|
||||||
if (docs instanceof Array) {
|
result = addDocs(docs, result);
|
||||||
for (const doc of docs) {
|
|
||||||
if (typeof doc !== 'string') {
|
|
||||||
throw new Error('Docs field (documentation url) must be an array of strings');
|
|
||||||
}
|
|
||||||
result.add(doc);
|
|
||||||
}
|
|
||||||
} else if (typeof docs === 'string') {
|
|
||||||
result.add(docs);
|
|
||||||
} else {
|
|
||||||
throw new Error('Docs field (documentation url) must a string or array of strings');
|
|
||||||
}
|
|
||||||
return result.getAll();
|
return result.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
class DocumentationUrls {
|
function addDocs(docs: DocumentationUrls, urls: DocumentationUrlContainer): DocumentationUrlContainer {
|
||||||
|
if (docs instanceof Array) {
|
||||||
|
urls.addUrls(docs);
|
||||||
|
} else if (typeof docs === 'string') {
|
||||||
|
urls.addUrl(docs);
|
||||||
|
} else {
|
||||||
|
throw new Error('Docs field (documentation url) must a string or array of strings');
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocumentationUrlContainer {
|
||||||
private readonly urls = new Array<string>();
|
private readonly urls = new Array<string>();
|
||||||
|
|
||||||
public add(url: string) {
|
public addUrl(url: string) {
|
||||||
validateUrl(url);
|
validateUrl(url);
|
||||||
this.urls.push(url);
|
this.urls.push(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addUrls(urls: any[]) {
|
||||||
|
for (const url of urls) {
|
||||||
|
if (typeof url !== 'string') {
|
||||||
|
throw new Error('Docs field (documentation url) must be an array of strings');
|
||||||
|
}
|
||||||
|
this.addUrl(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getAll(): ReadonlyArray<string> {
|
public getAll(): ReadonlyArray<string> {
|
||||||
return this.urls;
|
return this.urls;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -351,6 +351,26 @@ actions:
|
|||||||
schtasks /change /TN "\Microsoft\Windows\Customer Experience Improvement Program\Consolidator" /ENABLE
|
schtasks /change /TN "\Microsoft\Windows\Customer Experience Improvement Program\Consolidator" /ENABLE
|
||||||
schtasks /change /TN "\Microsoft\Windows\Customer Experience Improvement Program\KernelCeipTask" /ENABLE
|
schtasks /change /TN "\Microsoft\Windows\Customer Experience Improvement Program\KernelCeipTask" /ENABLE
|
||||||
schtasks /change /TN "\Microsoft\Windows\Customer Experience Improvement Program\UsbCeip" /ENABLE
|
schtasks /change /TN "\Microsoft\Windows\Customer Experience Improvement Program\UsbCeip" /ENABLE
|
||||||
|
-
|
||||||
|
name: Disable Webcam Telemetry (devicecensus.exe)
|
||||||
|
recommend: true
|
||||||
|
docs: https://www.ghacks.net/2019/09/23/what-is-devicecensus-exe-on-windows-10-and-why-does-it-need-internet-connectivity/
|
||||||
|
code: schtasks /change /TN "Microsoft\Windows\Device Information\Device" /DISABLE
|
||||||
|
revertCode: schtasks /change /TN "Microsoft\Windows\Device Information\Device" /ENABLE
|
||||||
|
-
|
||||||
|
name: Disable Application Experience (Compatibility Telemetry)
|
||||||
|
recommend: true
|
||||||
|
code: |-
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" /DISABLE
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\ProgramDataUpdater" /DISABLE
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\StartupAppTask" /DISABLE
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\AitAgent" /DISABLE
|
||||||
|
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\CompatTelRunner.exe" /v Debugger /t REG_SZ /d "%windir%\System32\taskkill.exe" /f
|
||||||
|
revertCode: |-
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" /ENABLE
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\ProgramDataUpdater" /ENABLE
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\StartupAppTask" /ENABLE
|
||||||
|
schtasks /change /TN "Microsoft\Windows\Application Experience\AitAgent" /ENABLE
|
||||||
-
|
-
|
||||||
name: Disable telemetry in data collection policy
|
name: Disable telemetry in data collection policy
|
||||||
recommend: true
|
recommend: true
|
||||||
@@ -368,16 +388,33 @@ actions:
|
|||||||
name: Disable error reporting
|
name: Disable error reporting
|
||||||
recommend: true
|
recommend: true
|
||||||
code: |-
|
code: |-
|
||||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Error Reporting" /v "Disabled" /t REG_DWORD /d "1" /f
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Error Reporting" /v "Disabled" /f
|
||||||
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v "Disabled" /t "REG_DWORD" /d "1" /f
|
reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v "Disabled" /t "REG_DWORD" /d "1" /f
|
||||||
sc stop "WerSvc" & sc config "WerSvc" start=disabled
|
sc stop "WerSvc" & sc config "WerSvc" start=disabled
|
||||||
sc stop "wercplsupport" & sc config "wercplsupport" start=disabled
|
sc stop "wercplsupport" & sc config "wercplsupport" start=disabled
|
||||||
|
revertCode: |-
|
||||||
|
reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Error Reporting" /v "Disabled" /f
|
||||||
|
reg delete "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v "Disabled" /f
|
||||||
|
sc config "WerSvc" start=demand
|
||||||
|
sc config "wercplsupport" start=demand
|
||||||
-
|
-
|
||||||
name: Disable online device metadata collection
|
name: Disable online device metadata collection
|
||||||
recommend: true
|
recommend: false
|
||||||
|
docs:
|
||||||
|
- https://www.stigviewer.com/stig/windows_server_2012_member_server/2014-01-07/finding/V-21964
|
||||||
|
- https://docs.microsoft.com/en-us/windows/client-management/mdm/policy-csp-deviceinstallation#deviceinstallation-preventdevicemetadatafromnetwork
|
||||||
code: |-
|
code: |-
|
||||||
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Device Metadata" /v "PreventDeviceMetadataFromNetwork" /t REG_DWORD /d 1 /f
|
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Device Metadata" /v "PreventDeviceMetadataFromNetwork" /t REG_DWORD /d 1 /f
|
||||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" /v "PreventDeviceMetadataFromNetwork" /t REG_DWORD /d 1 /f
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" /v "PreventDeviceMetadataFromNetwork" /t REG_DWORD /d 1 /f
|
||||||
|
revertCode: |-
|
||||||
|
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Device Metadata" /v "PreventDeviceMetadataFromNetwork" /t REG_DWORD /d 0 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Device Metadata" /v "PreventDeviceMetadataFromNetwork" /t REG_DWORD /d 0 /f
|
||||||
|
-
|
||||||
|
name: Disable cloud speech recognation
|
||||||
|
recommend: true
|
||||||
|
docs: https://www.tenforums.com/tutorials/101902-turn-off-online-speech-recognition-windows-10-a.html
|
||||||
|
code: reg add "HKCU\Software\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy" /v "HasAccepted" /t "REG_DWORD" /d 0 /f
|
||||||
|
revertCode: reg add "HKCU\Software\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy" /v "HasAccepted" /t "REG_DWORD" /d 1 /f
|
||||||
-
|
-
|
||||||
name: Disable active prompting (pings to MSFT NCSI server)
|
name: Disable active prompting (pings to MSFT NCSI server)
|
||||||
recommend: false
|
recommend: false
|
||||||
@@ -701,13 +738,6 @@ actions:
|
|||||||
reg add "HKLM\SOFTWARE\Microsoft\PolicyManager\default\System\AllowExperimentation" /v "value" /t "REG_DWORD" /d 0 /f
|
reg add "HKLM\SOFTWARE\Microsoft\PolicyManager\default\System\AllowExperimentation" /v "value" /t "REG_DWORD" /d 0 /f
|
||||||
reg add "HKLM\SOFTWARE\Microsoft\WindowsSelfHost\UI\Visibility" /v "HideInsiderPage" /t "REG_DWORD" /d "1" /f
|
reg add "HKLM\SOFTWARE\Microsoft\WindowsSelfHost\UI\Visibility" /v "HideInsiderPage" /t "REG_DWORD" /d "1" /f
|
||||||
sc stop "wisvc" & sc config "wisvc" start=disabled
|
sc stop "wisvc" & sc config "wisvc" start=disabled
|
||||||
-
|
|
||||||
name: Disable the Windows Connect Now wizard
|
|
||||||
recommend: false
|
|
||||||
docs:
|
|
||||||
- https://docs.microsoft.com/en-us/windows/win32/wcn/about-windows-connect-now
|
|
||||||
- https://www.windows-security.org/f637a705712eb59f8cd410673c96472e/prohibit-access-of-the-windows-connect-now-wizards
|
|
||||||
code: reg add "HKCU\Software\Policies\Microsoft\Windows\WCN\UI" /v "DisableWcnUi" /t REG_DWORD /d 1 /f
|
|
||||||
-
|
-
|
||||||
category: Disable cloud sync
|
category: Disable cloud sync
|
||||||
children:
|
children:
|
||||||
@@ -834,9 +864,10 @@ actions:
|
|||||||
recommend: true
|
recommend: true
|
||||||
code: reg add "HKLM\SOFTWARE\Policies\Microsoft\MRT" /v "DontReportInfectionInformation" /t REG_DWORD /d 1 /f
|
code: reg add "HKLM\SOFTWARE\Policies\Microsoft\MRT" /v "DontReportInfectionInformation" /t REG_DWORD /d 1 /f
|
||||||
-
|
-
|
||||||
name: Disable NetCore Cli telemetry
|
name: Disable NET Core CLI telemetry
|
||||||
recommend: true
|
recommend: true
|
||||||
code: setx DOTNET_CLI_TELEMETRY_OPTOUT 1
|
code: setx DOTNET_CLI_TELEMETRY_OPTOUT 1
|
||||||
|
revertCode: setx DOTNET_CLI_TELEMETRY_OPTOUT 0
|
||||||
-
|
-
|
||||||
name: Disable NVIDIA telemetry
|
name: Disable NVIDIA telemetry
|
||||||
recommend: true
|
recommend: true
|
||||||
@@ -1151,6 +1182,26 @@ actions:
|
|||||||
code: |-
|
code: |-
|
||||||
dism /online /Disable-Feature /FeatureName:"MicrosoftWindowsPowerShellV2Root" /NoRestart
|
dism /online /Disable-Feature /FeatureName:"MicrosoftWindowsPowerShellV2Root" /NoRestart
|
||||||
dism /online /Disable-Feature /FeatureName:"MicrosoftWindowsPowerShellV2" /NoRestart
|
dism /online /Disable-Feature /FeatureName:"MicrosoftWindowsPowerShellV2" /NoRestart
|
||||||
|
-
|
||||||
|
name: Disable the Windows Connect Now wizard
|
||||||
|
recommend: true
|
||||||
|
docs:
|
||||||
|
- https://docs.microsoft.com/en-us/windows/win32/wcn/about-windows-connect-now
|
||||||
|
- https://www.stigviewer.com/stig/windows_server_20122012_r2_domain_controller/2019-01-16/finding/V-15698
|
||||||
|
code: |-
|
||||||
|
reg add "HKLM\Software\Policies\Microsoft\Windows\WCN\UI" /v "DisableWcnUi" /t REG_DWORD /d 1 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableFlashConfigRegistrar" /t REG_DWORD /d 0 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableInBand802DOT11Registrar" /t REG_DWORD /d 0 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableUPnPRegistrar" /t REG_DWORD /d 0 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableWPDRegistrar" /t REG_DWORD /d 0 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "EnableRegistrars" /t REG_DWORD /d 0 /f
|
||||||
|
revertCode: |-
|
||||||
|
reg add "HKLM\Software\Policies\Microsoft\Windows\WCN\UI" /v "DisableWcnUi" /t REG_DWORD /d 0 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableFlashConfigRegistrar" /t REG_DWORD /d 1 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableInBand802DOT11Registrar" /t REG_DWORD /d 1 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableUPnPRegistrar" /t REG_DWORD /d 1 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "DisableWPDRegistrar" /t REG_DWORD /d 1 /f
|
||||||
|
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WCN\Registrars" /v "EnableRegistrars" /t REG_DWORD /d 1 /f
|
||||||
-
|
-
|
||||||
category: Privacy over security
|
category: Privacy over security
|
||||||
children:
|
children:
|
||||||
@@ -1249,11 +1300,13 @@ actions:
|
|||||||
-
|
-
|
||||||
name: Disable Sync Provider Notifications
|
name: Disable Sync Provider Notifications
|
||||||
recommend: false
|
recommend: false
|
||||||
code: REG ADD "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowSyncProviderNotifications" /d 0 /t REG_DWORD /f
|
code: reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowSyncProviderNotifications" /d 0 /t REG_DWORD /f
|
||||||
|
revertCode: reg add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowSyncProviderNotifications" /d 1 /t REG_DWORD /f
|
||||||
-
|
-
|
||||||
name: Turn hibernate off to disable sleep for quick start
|
name: Turn hibernate off to disable sleep for quick start
|
||||||
recommend: false
|
recommend: false
|
||||||
code: powercfg -h off
|
code: powercfg -h off
|
||||||
|
revertCode: powercfg -h on
|
||||||
docs: http://www.windows10windows7.com/w10/win10zs/100102504.html
|
docs: http://www.windows10windows7.com/w10/win10zs/100102504.html
|
||||||
-
|
-
|
||||||
category: Hide from This PC and Browse in dialog boxes
|
category: Hide from This PC and Browse in dialog boxes
|
||||||
@@ -1355,14 +1408,17 @@ actions:
|
|||||||
name: Xbox Live Auth Manager
|
name: Xbox Live Auth Manager
|
||||||
recommend: true
|
recommend: true
|
||||||
code: sc stop "XblAuthManager" & sc config "XblAuthManager" start=disabled
|
code: sc stop "XblAuthManager" & sc config "XblAuthManager" start=disabled
|
||||||
|
revetCode: sc config "XblAuthManager" start=demand
|
||||||
-
|
-
|
||||||
name: Xbox Live Game Save
|
name: Xbox Live Game Save
|
||||||
recommend: true
|
recommend: true
|
||||||
code: sc stop "XblGameSave" & sc config "XblGameSave" start=disabled
|
code: sc stop "XblGameSave" & sc config "XblGameSave" start=disabled
|
||||||
|
revertCode: sc config "XblGameSave" start=demand
|
||||||
-
|
-
|
||||||
name: Xbox Live Networking Service
|
name: Xbox Live Networking Service
|
||||||
recommend: true
|
recommend: true
|
||||||
code: sc stop "XboxNetApiSvc" & sc config "XboxNetApiSvc" start=disabled
|
code: sc stop "XboxNetApiSvc" & sc config "XboxNetApiSvc" start=disabled
|
||||||
|
revetCode: sc config "XboxNetApiSvc" start=demand
|
||||||
-
|
-
|
||||||
name: Windows Biometric Service
|
name: Windows Biometric Service
|
||||||
recommend: true
|
recommend: true
|
||||||
@@ -1781,15 +1837,17 @@ actions:
|
|||||||
-
|
-
|
||||||
name: Remove OneDrive
|
name: Remove OneDrive
|
||||||
code: |-
|
code: |-
|
||||||
taskkill /F /IM OneDrive.exe
|
taskkill /f /im OneDrive.exe
|
||||||
%SystemRoot%\System32\OneDriveSetup.exe /uninstall
|
%SystemRoot%\System32\OneDriveSetup.exe /uninstall
|
||||||
%SystemRoot%\SysWOW64\OneDriveSetup.exe /uninstall
|
%SystemRoot%\SysWOW64\OneDriveSetup.exe /uninstall
|
||||||
rd "%UserProfile%\OneDrive" /Q /S
|
rd "%UserProfile%\OneDrive" /q /s
|
||||||
rd "%LocalAppData%\Microsoft\OneDrive" /Q /S
|
rd "%LocalAppData%\Microsoft\OneDrive" /q /s
|
||||||
rd "%ProgramData%\Microsoft OneDrive" /Q /S
|
rd "%ProgramData%\Microsoft OneDrive" /q /s
|
||||||
rd "C:\OneDriveTemp" /Q /S
|
rd "C:\OneDriveTemp" /q /s
|
||||||
|
del "%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk" /s /f /q
|
||||||
reg delete "HKEY_CLASSES_ROOT\CLSID{018D5C66-4533-4307-9B53-224DE2ED1FE6}" /f
|
reg delete "HKEY_CLASSES_ROOT\CLSID{018D5C66-4533-4307-9B53-224DE2ED1FE6}" /f
|
||||||
reg delete "HKEY_CLASSES_ROOT\Wow6432Node\CLSID{018D5C66-4533-4307-9B53-224DE2ED1FE6}" /f
|
reg delete "HKEY_CLASSES_ROOT\Wow6432Node\CLSID{018D5C66-4533-4307-9B53-224DE2ED1FE6}" /f
|
||||||
|
reg add "HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}" /v System.IsPinnedToNameSpaceTree /d "0" /t REG_DWORD /f
|
||||||
-
|
-
|
||||||
category: Disable built-in Windows features
|
category: Disable built-in Windows features
|
||||||
children:
|
children:
|
||||||
|
|||||||
2
src/application/application.yaml.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
declare module 'js-yaml-loader!*' {
|
declare module 'js-yaml-loader!*' {
|
||||||
export type CategoryOrScript = YamlCategory | YamlScript;
|
export type CategoryOrScript = YamlCategory | YamlScript;
|
||||||
type DocumentationUrls = ReadonlyArray<string> | string;
|
export type DocumentationUrls = ReadonlyArray<string> | string;
|
||||||
|
|
||||||
export interface YamlDocumentable {
|
export interface YamlDocumentable {
|
||||||
docs?: DocumentationUrls;
|
docs?: DocumentationUrls;
|
||||||
|
|||||||
133
src/background.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { app, protocol, BrowserWindow, shell } from 'electron';
|
||||||
|
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
|
||||||
|
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
|
||||||
|
import path from 'path';
|
||||||
|
import { autoUpdater } from 'electron-updater';
|
||||||
|
import log from 'electron-log';
|
||||||
|
|
||||||
|
|
||||||
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
|
declare const __static: string; // https://github.com/electron-userland/electron-webpack/issues/172
|
||||||
|
|
||||||
|
// Keep a global reference of the window object, if you don't, the window will
|
||||||
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
|
let win: BrowserWindow | null;
|
||||||
|
|
||||||
|
// Scheme must be registered before the app is ready
|
||||||
|
protocol.registerSchemesAsPrivileged([
|
||||||
|
{ scheme: 'app', privileges: { secure: true, standard: true } },
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Setup logging
|
||||||
|
autoUpdater.logger = log; // https://www.electron.build/auto-update#debugging
|
||||||
|
log.transports.file.level = 'silly';
|
||||||
|
if (!process.env.IS_TEST) {
|
||||||
|
Object.assign(console, log.functions); // override console.log, console.warn etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
// Create the browser window.
|
||||||
|
win = new BrowserWindow({
|
||||||
|
width: 1350,
|
||||||
|
height: 955,
|
||||||
|
webPreferences: {
|
||||||
|
// Use pluginOptions.nodeIntegration, leave this alone
|
||||||
|
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
|
||||||
|
nodeIntegration: (process.env
|
||||||
|
.ELECTRON_NODE_INTEGRATION as unknown) as boolean,
|
||||||
|
},
|
||||||
|
// https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#icons
|
||||||
|
icon: path.join(__static, `favicon.ico`),
|
||||||
|
});
|
||||||
|
|
||||||
|
win.setMenuBarVisibility(false);
|
||||||
|
configureExternalsUrlsOpenBrowser(win);
|
||||||
|
loadApplication(win);
|
||||||
|
|
||||||
|
win.on('closed', () => {
|
||||||
|
win = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit when all windows are closed.
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
// On macOS it is common for applications and their menu bar
|
||||||
|
// to stay active until the user quits explicitly with Cmd + Q
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
// On macOS it's common to re-create a window in the app when the
|
||||||
|
// dock icon is clicked and there are no other windows open.
|
||||||
|
if (win === null) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// This method will be called when Electron has finished
|
||||||
|
// initialization and is ready to create browser windows.
|
||||||
|
// Some APIs can only be used after this event occurs.
|
||||||
|
app.on('ready', async () => {
|
||||||
|
if (isDevelopment && !process.env.IS_TEST) {
|
||||||
|
// Install Vue Devtools
|
||||||
|
try {
|
||||||
|
await installExtension(VUEJS_DEVTOOLS);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Vue Devtools failed to install:', e.toString()); // tslint:disable-line:no-console
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createWindow();
|
||||||
|
});
|
||||||
|
|
||||||
|
// See electron-builder issue "checkForUpdatesAndNotify updates but does not notify on Windows 10"
|
||||||
|
// https://github.com/electron-userland/electron-builder/issues/2700
|
||||||
|
// https://github.com/electron/electron/issues/10864
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/shell/appid#how-to-form-an-application-defined-appusermodelid
|
||||||
|
app.setAppUserModelId('Undergroundwires.PrivacySexy');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit cleanly on request from parent process in development mode.
|
||||||
|
if (isDevelopment) {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
process.on('message', (data) => {
|
||||||
|
if (data === 'graceful-exit') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadApplication(window: BrowserWindow) {
|
||||||
|
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||||
|
// Load the url of the dev server if in development mode
|
||||||
|
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string);
|
||||||
|
if (!process.env.IS_TEST) {
|
||||||
|
win.webContents.openDevTools();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
createProtocol('app');
|
||||||
|
// Load the index.html when not in development
|
||||||
|
win.loadURL('app://./index.html');
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
autoUpdater.checkForUpdatesAndNotify(); // https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#check-for-updates-in-background-js-ts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureExternalsUrlsOpenBrowser(window: BrowserWindow) {
|
||||||
|
window.webContents.on('new-window', (event, url) => { // handle redirect
|
||||||
|
if (url !== win.webContents.getURL()) {
|
||||||
|
event.preventDefault();
|
||||||
|
shell.openExternal(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -18,15 +18,7 @@ export class Application implements IApplication {
|
|||||||
if (!repositoryUrl) { throw Error('Application has no repository url'); }
|
if (!repositoryUrl) { throw Error('Application has no repository url'); }
|
||||||
if (!version) { throw Error('Version cannot be empty'); }
|
if (!version) { throw Error('Version cannot be empty'); }
|
||||||
this.flattened = flatten(actions);
|
this.flattened = flatten(actions);
|
||||||
if (this.flattened.allCategories.length === 0) {
|
ensureValid(this.flattened);
|
||||||
throw new Error('Application must consist of at least one category');
|
|
||||||
}
|
|
||||||
if (this.flattened.allScripts.length === 0) {
|
|
||||||
throw new Error('Application must consist of at least one script');
|
|
||||||
}
|
|
||||||
if (this.flattened.allScripts.filter((script) => script.isRecommended).length === 0) {
|
|
||||||
throw new Error('Application must consist of at least one recommended script');
|
|
||||||
}
|
|
||||||
ensureNoDuplicates(this.flattened.allCategories);
|
ensureNoDuplicates(this.flattened.allCategories);
|
||||||
ensureNoDuplicates(this.flattened.allScripts);
|
ensureNoDuplicates(this.flattened.allScripts);
|
||||||
}
|
}
|
||||||
@@ -75,30 +67,50 @@ interface IFlattenedApplication {
|
|||||||
allScripts: IScript[];
|
allScripts: IScript[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenRecursive(
|
function ensureValid(application: IFlattenedApplication) {
|
||||||
|
if (!application.allCategories || application.allCategories.length === 0) {
|
||||||
|
throw new Error('Application must consist of at least one category');
|
||||||
|
}
|
||||||
|
if (!application.allScripts || application.allScripts.length === 0) {
|
||||||
|
throw new Error('Application must consist of at least one script');
|
||||||
|
}
|
||||||
|
if (application.allScripts.filter((script) => script.isRecommended).length === 0) {
|
||||||
|
throw new Error('Application must consist of at least one recommended script');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenCategories(
|
||||||
categories: ReadonlyArray<ICategory>,
|
categories: ReadonlyArray<ICategory>,
|
||||||
flattened: IFlattenedApplication) {
|
flattened: IFlattenedApplication): IFlattenedApplication {
|
||||||
|
if (!categories || categories.length === 0) {
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
flattened.allCategories.push(category);
|
flattened.allCategories.push(category);
|
||||||
if (category.scripts) {
|
flattened = flattenScripts(category.scripts, flattened);
|
||||||
for (const script of category.scripts) {
|
flattened = flattenCategories(category.subCategories, flattened);
|
||||||
flattened.allScripts.push(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (category.subCategories && category.subCategories.length > 0) {
|
|
||||||
flattenRecursive(
|
|
||||||
category.subCategories as ReadonlyArray<ICategory>,
|
|
||||||
flattened);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenScripts(
|
||||||
|
scripts: ReadonlyArray<IScript>,
|
||||||
|
flattened: IFlattenedApplication): IFlattenedApplication {
|
||||||
|
if (!scripts) {
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
|
for (const script of scripts) {
|
||||||
|
flattened.allScripts.push(script);
|
||||||
|
}
|
||||||
|
return flattened;
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatten(
|
function flatten(
|
||||||
categories: ReadonlyArray<ICategory>): IFlattenedApplication {
|
categories: ReadonlyArray<ICategory>): IFlattenedApplication {
|
||||||
const flattened: IFlattenedApplication = {
|
let flattened: IFlattenedApplication = {
|
||||||
allCategories: new Array<ICategory>(),
|
allCategories: new Array<ICategory>(),
|
||||||
allScripts: new Array<IScript>(),
|
allScripts: new Array<IScript>(),
|
||||||
};
|
};
|
||||||
flattenRecursive(categories, flattened);
|
flattened = flattenCategories(categories, flattened);
|
||||||
return flattened;
|
return flattened;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,20 @@ import { faGithub } from '@fortawesome/free-brands-svg-icons';
|
|||||||
/** BRAND ICONS (PREFIX: fab) */
|
/** BRAND ICONS (PREFIX: fab) */
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
/** REGULAR ICONS (PREFIX: far) */
|
/** REGULAR ICONS (PREFIX: far) */
|
||||||
import { faFolderOpen, faFolder } from '@fortawesome/free-regular-svg-icons';
|
import { faFolderOpen, faFolder, faSmile } from '@fortawesome/free-regular-svg-icons';
|
||||||
/** SOLID ICONS (PREFIX: fas (default)) */
|
/** SOLID ICONS (PREFIX: fas (default)) */
|
||||||
import { faTimes, faFileDownload, faCopy, faSearch, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
import { faTimes, faFileDownload, faCopy, faSearch, faInfoCircle, faUserSecret, faDesktop, faTag, faGlobe } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
|
||||||
export class IconBootstrapper implements IVueBootstrapper {
|
export class IconBootstrapper implements IVueBootstrapper {
|
||||||
public bootstrap(vue: VueConstructor): void {
|
public bootstrap(vue: VueConstructor): void {
|
||||||
library.add(
|
library.add(
|
||||||
faGithub,
|
faGithub,
|
||||||
|
faUserSecret,
|
||||||
|
faSmile,
|
||||||
|
faDesktop,
|
||||||
|
faGlobe,
|
||||||
|
faTag,
|
||||||
faFolderOpen,
|
faFolderOpen,
|
||||||
faFolder,
|
faFolder,
|
||||||
faTimes,
|
faTimes,
|
||||||
|
|||||||
@@ -75,10 +75,7 @@ export default class CardListItem extends StatefulVue {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/presentation/styles/colors.scss";
|
@import "@/presentation/styles/colors.scss";
|
||||||
|
@import "@/presentation/styles/media.scss";
|
||||||
$big-screen-width: 991px;
|
|
||||||
$medium-screen-width: 767px;
|
|
||||||
$small-screen-width: 380px;
|
|
||||||
|
|
||||||
$card-padding: 30px;
|
$card-padding: 30px;
|
||||||
$card-margin: 15px;
|
$card-margin: 15px;
|
||||||
|
|||||||
@@ -30,19 +30,32 @@ export function getCategoryNodeId(category: ICategory): string {
|
|||||||
|
|
||||||
function parseCategoryRecursively(
|
function parseCategoryRecursively(
|
||||||
parentCategory: ICategory): INode[] {
|
parentCategory: ICategory): INode[] {
|
||||||
if (!parentCategory) { throw new Error('parentCategory is undefined'); }
|
if (!parentCategory) {
|
||||||
|
throw new Error('parentCategory is undefined');
|
||||||
const nodes = new Array<INode>();
|
|
||||||
if (parentCategory.subCategories && parentCategory.subCategories.length > 0) {
|
|
||||||
for (const subCategory of parentCategory.subCategories) {
|
|
||||||
const subCategoryNodes = parseCategoryRecursively(subCategory);
|
|
||||||
nodes.push(convertCategoryToNode(subCategory, subCategoryNodes));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (parentCategory.scripts && parentCategory.scripts.length > 0) {
|
let nodes = new Array<INode>();
|
||||||
for (const script of parentCategory.scripts) {
|
nodes = addCategories(parentCategory.subCategories, nodes);
|
||||||
nodes.push(convertScriptToNode(script));
|
nodes = addScripts(parentCategory.scripts, nodes);
|
||||||
}
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addScripts(scripts: ReadonlyArray<IScript>, nodes: INode[]): INode[] {
|
||||||
|
if (!scripts || scripts.length === 0) {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
for (const script of scripts) {
|
||||||
|
nodes.push(convertScriptToNode(script));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCategories(categories: ReadonlyArray<ICategory>, nodes: INode[]): INode[] {
|
||||||
|
if (!categories || categories.length === 0) {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
for (const category of categories) {
|
||||||
|
const subCategoryNodes = parseCategoryRecursively(category);
|
||||||
|
nodes.push(convertCategoryToNode(category, subCategoryNodes));
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ export function convertExistingToNode(liquorTreeNode: ILiquorTreeExistingNode):
|
|||||||
id: liquorTreeNode.id,
|
id: liquorTreeNode.id,
|
||||||
text: liquorTreeNode.data.text,
|
text: liquorTreeNode.data.text,
|
||||||
// selected: liquorTreeNode.states && liquorTreeNode.states.checked,
|
// selected: liquorTreeNode.states && liquorTreeNode.states.checked,
|
||||||
children: (!liquorTreeNode.children || liquorTreeNode.children.length === 0)
|
children: convertChildren(liquorTreeNode.children, convertExistingToNode),
|
||||||
? [] : liquorTreeNode.children.map((childNode) => convertExistingToNode(childNode)),
|
|
||||||
documentationUrls: liquorTreeNode.data.documentationUrls,
|
documentationUrls: liquorTreeNode.data.documentationUrls,
|
||||||
isReversible : liquorTreeNode.data.isReversible,
|
isReversible : liquorTreeNode.data.isReversible,
|
||||||
};
|
};
|
||||||
@@ -24,11 +23,19 @@ export function toNewLiquorTreeNode(node: INode): ILiquorTreeNewNode {
|
|||||||
state: {
|
state: {
|
||||||
checked: false,
|
checked: false,
|
||||||
},
|
},
|
||||||
children: (!node.children || node.children.length === 0) ? [] :
|
children: convertChildren(node.children, toNewLiquorTreeNode),
|
||||||
node.children.map((childNode) => toNewLiquorTreeNode(childNode)),
|
|
||||||
data: {
|
data: {
|
||||||
documentationUrls: node.documentationUrls,
|
documentationUrls: node.documentationUrls,
|
||||||
isReversible: node.isReversible,
|
isReversible: node.isReversible,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertChildren<TOldNode, TNewNode>(
|
||||||
|
oldChildren: readonly TOldNode[],
|
||||||
|
callback: (value: TOldNode) => TNewNode): TNewNode[] {
|
||||||
|
if (!oldChildren || oldChildren.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return oldChildren.map((childNode) => callback(childNode));
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,16 +13,16 @@ const NothingChosenCode =
|
|||||||
new CodeBuilder()
|
new CodeBuilder()
|
||||||
.appendCommentLine('privacy.sexy — 🔐 Enforce privacy & security best-practices on Windows')
|
.appendCommentLine('privacy.sexy — 🔐 Enforce privacy & security best-practices on Windows')
|
||||||
.appendLine()
|
.appendLine()
|
||||||
|
.appendCommentLine('-- 🤔 How to use')
|
||||||
|
.appendCommentLine(' 📙 Start by exploring different categories and choosing different tweaks.')
|
||||||
|
.appendCommentLine(' 📙 You can select "Recommended" on the top to select "safer" tweaks. Always double check!')
|
||||||
|
.appendCommentLine(' 📙 After you choose any tweak, you can download & copy to execute your script.')
|
||||||
|
.appendLine()
|
||||||
.appendCommentLine('-- 🧐 Why privacy.sexy')
|
.appendCommentLine('-- 🧐 Why privacy.sexy')
|
||||||
.appendCommentLine(' ✔️ Rich tweak pool to harden security & privacy of the OS and other softwares on it.')
|
.appendCommentLine(' ✔️ Rich tweak pool to harden security & privacy of the OS and other softwares on it.')
|
||||||
.appendCommentLine(' ✔️ You don\'t need to run any compiled software on your system, just run the generated scripts.')
|
.appendCommentLine(' ✔️ You don\'t need to run any compiled software on your system, just run the generated scripts.')
|
||||||
.appendCommentLine(' ✔️ Have full visibility into what the tweaks do as you enable them.')
|
.appendCommentLine(' ✔️ Have full visibility into what the tweaks do as you enable them.')
|
||||||
.appendCommentLine(' ✔️ Free software, 100% transparency: both application & infrastructure code are open-sourced.')
|
.appendCommentLine(' ✔️ Free software, 100% transparency: both application & infrastructure code are open-sourced.')
|
||||||
.appendLine()
|
|
||||||
.appendCommentLine('-- 🤔 How to use')
|
|
||||||
.appendCommentLine(' 📙 Start by exploring different categories and choosing different tweaks.')
|
|
||||||
.appendCommentLine(' 📙 You can select "Recommended" on the top to select "safer" tweaks. Always double check!')
|
|
||||||
.appendCommentLine(' 📙 After you choose any tweak, you can download & copy to execute your script.')
|
|
||||||
.toString();
|
.toString();
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="footer">
|
|
||||||
<div class="item">
|
|
||||||
<a :href="releaseUrl" target="_blank">{{ version }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<a @click="$modal.show(modalName)">Privacy</a> <!-- href to #privacy to avoid scrolling to top -->
|
|
||||||
</div>
|
|
||||||
<modal :name="modalName" height="auto" :scrollable="true" :adaptive="true">
|
|
||||||
<div class="modal">
|
|
||||||
<ThePrivacyPolicy class="modal__content"/>
|
|
||||||
<div class="modal__close-button">
|
|
||||||
<font-awesome-icon :icon="['fas', 'times']" @click="$modal.hide(modalName)"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</modal>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
|
||||||
import { StatefulVue } from './StatefulVue';
|
|
||||||
import ThePrivacyPolicy from './ThePrivacyPolicy.vue';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
components: {
|
|
||||||
ThePrivacyPolicy,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
export default class TheFooter extends StatefulVue {
|
|
||||||
private readonly modalName = 'privacy-policy';
|
|
||||||
private version: string = '';
|
|
||||||
private releaseUrl: string = '';
|
|
||||||
|
|
||||||
public async mounted() {
|
|
||||||
const state = await this.getCurrentStateAsync();
|
|
||||||
this.version = `v${state.app.version}`;
|
|
||||||
this.releaseUrl = `${state.app.repositoryUrl}/releases/tag/${state.app.version}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
@import "@/presentation/styles/colors.scss";
|
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
color: $dark-gray;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-family: $normal-font;
|
|
||||||
align-self: center;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color:inherit;
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:not(:first-child)::before {
|
|
||||||
content: "|";
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.modal {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__close-button {
|
|
||||||
width: auto;
|
|
||||||
font-size: 1.5em;
|
|
||||||
margin-right:0.25em;
|
|
||||||
align-self: flex-start;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
72
src/presentation/TheFooter/DownloadUrlList.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<span
|
||||||
|
class="container"
|
||||||
|
v-bind:class="{ 'container_unsupported': !hasCurrentOsDesktopVersion, 'container_supported': hasCurrentOsDesktopVersion }">
|
||||||
|
<span class="description">
|
||||||
|
<font-awesome-icon class="description__icon" :icon="['fas', 'desktop']" />
|
||||||
|
<span class="description__text">For desktop:</span>
|
||||||
|
</span>
|
||||||
|
<span class="urls">
|
||||||
|
<span class="urls__url" v-for="os of supportedDesktops" v-bind:key="os">
|
||||||
|
<DownloadUrlListItem :operatingSystem="os" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { Environment } from '@/application/Environment/Environment';
|
||||||
|
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||||
|
import DownloadUrlListItem from './DownloadUrlListItem.vue';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: { DownloadUrlListItem },
|
||||||
|
})
|
||||||
|
export default class DownloadUrlList extends Vue {
|
||||||
|
public readonly supportedDesktops: ReadonlyArray<OperatingSystem>;
|
||||||
|
public readonly hasCurrentOsDesktopVersion: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const supportedOperativeSystems = [OperatingSystem.Windows, OperatingSystem.Linux, OperatingSystem.macOS];
|
||||||
|
const currentOs = Environment.CurrentEnvironment.os;
|
||||||
|
this.supportedDesktops = supportedOperativeSystems.sort((os) => os === currentOs ? 0 : 1);
|
||||||
|
this.hasCurrentOsDesktopVersion = supportedOperativeSystems.includes(currentOs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/presentation/styles/media.scss";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display:flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
&_unsupported {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
&_supported {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
.description {
|
||||||
|
&__icon {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
&__text {
|
||||||
|
margin-right: 0.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.urls {
|
||||||
|
&__url {
|
||||||
|
&:not(:first-child)::before {
|
||||||
|
opacity: 0.5;
|
||||||
|
content: "|";
|
||||||
|
font-size: 0.6rem;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
94
src/presentation/TheFooter/DownloadUrlListItem.vue
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<span class="url">
|
||||||
|
<a :href="downloadUrl"
|
||||||
|
v-bind:class="{
|
||||||
|
'url__active': hasCurrentOsDesktopVersion && isCurrentOs,
|
||||||
|
'url__inactive': hasCurrentOsDesktopVersion && !isCurrentOs,
|
||||||
|
}">{{ operatingSystemName }}</a>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||||
|
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||||
|
import { Environment } from '@/application/Environment/Environment';
|
||||||
|
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class DownloadUrlListItem extends StatefulVue {
|
||||||
|
@Prop() public operatingSystem!: OperatingSystem;
|
||||||
|
public OperatingSystem = OperatingSystem;
|
||||||
|
|
||||||
|
public downloadUrl: string = '';
|
||||||
|
public operatingSystemName: string = '';
|
||||||
|
public isCurrentOs: boolean = false;
|
||||||
|
public hasCurrentOsDesktopVersion: boolean = false;
|
||||||
|
|
||||||
|
public async mounted() {
|
||||||
|
await this.onOperatingSystemChangedAsync(this.operatingSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Watch('operatingSystem')
|
||||||
|
public async onOperatingSystemChangedAsync(os: OperatingSystem) {
|
||||||
|
const currentOs = Environment.CurrentEnvironment.os;
|
||||||
|
this.isCurrentOs = os === currentOs;
|
||||||
|
this.downloadUrl = await this.getDownloadUrlAsync(os);
|
||||||
|
this.operatingSystemName = getOperatingSystemName(os);
|
||||||
|
this.hasCurrentOsDesktopVersion = hasDesktopVersion(currentOs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getDownloadUrlAsync(os: OperatingSystem): Promise<string> {
|
||||||
|
const state = await this.getCurrentStateAsync();
|
||||||
|
return `${state.app.repositoryUrl}/releases/download/${state.app.version}/${getFileName(os, state.app.version)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasDesktopVersion(os: OperatingSystem): boolean {
|
||||||
|
return os === OperatingSystem.Windows
|
||||||
|
|| os === OperatingSystem.Linux
|
||||||
|
|| os === OperatingSystem.macOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOperatingSystemName(os: OperatingSystem): string {
|
||||||
|
switch (os) {
|
||||||
|
case OperatingSystem.Linux:
|
||||||
|
return 'Linux';
|
||||||
|
case OperatingSystem.macOS:
|
||||||
|
return 'macOS';
|
||||||
|
case OperatingSystem.Windows:
|
||||||
|
return 'Windows';
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported os: ${OperatingSystem[os]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileName(os: OperatingSystem, version: string): string {
|
||||||
|
switch (os) {
|
||||||
|
case OperatingSystem.Linux:
|
||||||
|
return `privacy.sexy-${version}.AppImage`;
|
||||||
|
case OperatingSystem.macOS:
|
||||||
|
return `privacy.sexy-${version}.dmg`;
|
||||||
|
case OperatingSystem.Windows:
|
||||||
|
return `privacy.sexy-Setup-${version}.exe`;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported os: ${OperatingSystem[os]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.url {
|
||||||
|
&__active {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
&__inactive {
|
||||||
|
font-size: 0.70em;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color:inherit;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
81
src/presentation/TheFooter/PrivacyPolicy.vue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div class="privacy-policy">
|
||||||
|
<div v-if="!isDesktop" class="line">
|
||||||
|
<div class="line__emoji">🚫🍪</div>
|
||||||
|
<div>No cookies!</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="isDesktop" class="line">
|
||||||
|
<div class="line__emoji">🚫🌐</div>
|
||||||
|
<div>Everything is offline, except single request GitHub to check for updates on application start.</div>
|
||||||
|
</div>
|
||||||
|
<div class="line">
|
||||||
|
<div class="line__emoji">🚫👀</div>
|
||||||
|
<div>No user behavior / IP adress collection!</div>
|
||||||
|
</div>
|
||||||
|
<div class="line">
|
||||||
|
<div class="line__emoji">🤖</div>
|
||||||
|
<div>All transparent: Deployed automatically from master branch
|
||||||
|
of the <a :href="repositoryUrl" target="_blank">source code</a> with no changes.</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!isDesktop" class="line">
|
||||||
|
<div class="line__emoji">📈</div>
|
||||||
|
<div>Basic <a href="https://aws.amazon.com/cloudfront/reporting/" target="_blank">CDN statistics</a>
|
||||||
|
are collected by AWS but they cannot be related to you or your behavior. You can download the offline version if you don't want CDN data collection.</div>
|
||||||
|
</div>
|
||||||
|
<div class="line">
|
||||||
|
<div class="line__emoji">🎉</div>
|
||||||
|
<div>As almost no data is colected, the application gets better only with your active feedback.
|
||||||
|
Feel free to <a :href="feedbackUrl" target="_blank">create an issue</a> 😊</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||||
|
import { Environment } from '@/application/Environment/Environment';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class TheFooter extends StatefulVue {
|
||||||
|
public repositoryUrl: string = '';
|
||||||
|
public feedbackUrl: string = '';
|
||||||
|
public isDesktop: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.isDesktop = Environment.CurrentEnvironment.isDesktop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async mounted() {
|
||||||
|
const state = await this.getCurrentStateAsync();
|
||||||
|
this.repositoryUrl = state.app.repositoryUrl;
|
||||||
|
this.feedbackUrl = `${state.app.repositoryUrl}/issues`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/presentation/styles/fonts.scss";
|
||||||
|
.privacy-policy {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: $normal-font;
|
||||||
|
text-align:center;
|
||||||
|
|
||||||
|
.line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top:0.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color:inherit;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
159
src/presentation/TheFooter/TheFooter.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="footer__section">
|
||||||
|
<span v-if="isDesktop" class="footer__section__item">
|
||||||
|
<font-awesome-icon class="icon" :icon="['fas', 'globe']" />
|
||||||
|
<span>Online version at <a href="https://privacy.sexy" target="_blank">https://privacy.sexy</a></span>
|
||||||
|
</span>
|
||||||
|
<span v-else class="footer__section__item">
|
||||||
|
<DownloadUrlList />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="footer__section">
|
||||||
|
<div class="footer__section__item">
|
||||||
|
<a :href="feedbackUrl" target="_blank">
|
||||||
|
<font-awesome-icon class="icon" :icon="['far', 'smile']" />
|
||||||
|
<span>Feedback</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="footer__section__item">
|
||||||
|
<a :href="repositoryUrl" target="_blank">
|
||||||
|
<font-awesome-icon class="icon" :icon="['fab', 'github']" />
|
||||||
|
<span>Source Code</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="footer__section__item">
|
||||||
|
<a :href="releaseUrl" target="_blank">
|
||||||
|
<font-awesome-icon class="icon" :icon="['fas', 'tag']" />
|
||||||
|
<span>v{{ version }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="footer__section__item">
|
||||||
|
<font-awesome-icon class="icon" :icon="['fas', 'user-secret']" />
|
||||||
|
<a @click="$modal.show(modalName)">Privacy</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<modal :name="modalName" height="auto" :scrollable="true" :adaptive="true">
|
||||||
|
<div class="modal">
|
||||||
|
<PrivacyPolicy class="modal__content"/>
|
||||||
|
<div class="modal__close-button">
|
||||||
|
<font-awesome-icon :icon="['fas', 'times']" @click="$modal.hide(modalName)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||||
|
import { Environment } from '@/application/Environment/Environment';
|
||||||
|
import PrivacyPolicy from './PrivacyPolicy.vue';
|
||||||
|
import DownloadUrlList from './DownloadUrlList.vue';
|
||||||
|
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {
|
||||||
|
PrivacyPolicy, DownloadUrlList,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default class TheFooter extends StatefulVue {
|
||||||
|
public readonly modalName = 'privacy-policy';
|
||||||
|
public readonly isDesktop: boolean;
|
||||||
|
|
||||||
|
public version: string = '';
|
||||||
|
public repositoryUrl: string = '';
|
||||||
|
public releaseUrl: string = '';
|
||||||
|
public feedbackUrl: string = '';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.isDesktop = Environment.CurrentEnvironment.isDesktop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async mounted() {
|
||||||
|
const state = await this.getCurrentStateAsync();
|
||||||
|
this.version = state.app.version;
|
||||||
|
this.repositoryUrl = state.app.repositoryUrl;
|
||||||
|
this.releaseUrl = `${state.app.repositoryUrl}/releases/tag/${state.app.version}`;
|
||||||
|
this.feedbackUrl = `${state.app.repositoryUrl}/issues`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/presentation/styles/colors.scss";
|
||||||
|
@import "@/presentation/styles/fonts.scss";
|
||||||
|
@import "@/presentation/styles/media.scss";
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
@media (max-width: $big-screen-width) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
&__section {
|
||||||
|
display: flex;
|
||||||
|
@media (max-width: $big-screen-width) {
|
||||||
|
justify-content: space-around;
|
||||||
|
width:100%;
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: 0.7em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flex-wrap: wrap;
|
||||||
|
color: $dark-gray;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: $normal-font;
|
||||||
|
a {
|
||||||
|
color:inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__item:not(:first-child) {
|
||||||
|
&::before {
|
||||||
|
content: "|";
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
@media (max-width: $big-screen-width) {
|
||||||
|
margin-top: 3px;
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close-button {
|
||||||
|
width: auto;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-right:0.25em;
|
||||||
|
align-self: flex-start;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<h1 class="child title" >{{ title }}</h1>
|
<h1 class="child title" >{{ title }}</h1>
|
||||||
<h2 class="child subtitle">{{ subtitle }}</h2>
|
<h2 class="child subtitle">Enforce privacy & security on Windows</h2>
|
||||||
<a :href="repositoryUrl" target="_blank" class="child github" >
|
|
||||||
<font-awesome-icon :icon="['fab', 'github']" size="3x" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -16,13 +13,10 @@ import { StatefulVue } from './StatefulVue';
|
|||||||
export default class TheHeader extends StatefulVue {
|
export default class TheHeader extends StatefulVue {
|
||||||
public title = '';
|
public title = '';
|
||||||
public subtitle = '';
|
public subtitle = '';
|
||||||
public repositoryUrl = '';
|
|
||||||
|
|
||||||
public async mounted() {
|
public async mounted() {
|
||||||
const state = await this.getCurrentStateAsync();
|
const state = await this.getCurrentStateAsync();
|
||||||
this.title = state.app.name;
|
this.title = state.app.name;
|
||||||
this.subtitle = 'Enforce privacy & security on Windows';
|
|
||||||
this.repositoryUrl = state.app.repositoryUrl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -56,13 +50,6 @@ export default class TheHeader extends StatefulVue {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
.github {
|
|
||||||
color:inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="privacy-policy">
|
|
||||||
<div class="line">
|
|
||||||
<div class="line__emoji">🚫🍪</div>
|
|
||||||
<div>No cookies!</div>
|
|
||||||
</div>
|
|
||||||
<div class="line">
|
|
||||||
<div class="line__emoji">🚫👀</div>
|
|
||||||
<div>No user behavior / IP adress collection!</div>
|
|
||||||
</div>
|
|
||||||
<div class="line">
|
|
||||||
<div class="line__emoji">🤖</div>
|
|
||||||
<div>Website is deployed automatically from master branch
|
|
||||||
of the <a :href="repositoryUrl" target="_blank">source code</a> with no changes.</div>
|
|
||||||
</div>
|
|
||||||
<div class="line">
|
|
||||||
<div class="line__emoji">📈</div>
|
|
||||||
<div>Basic <a href="https://aws.amazon.com/cloudfront/reporting/" target="_blank">CDN statistics</a>
|
|
||||||
are collected by AWS but they cannot be related to you or your behavior.</div>
|
|
||||||
</div>
|
|
||||||
<div class="line">
|
|
||||||
<div class="line__emoji">🎉</div>
|
|
||||||
<div>As almost no data is colected, the website gets better only with your active feedback.
|
|
||||||
Feel free to <a :href="feedbackUrl" target="_blank">create an issue</a> 😊</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
|
||||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
|
||||||
|
|
||||||
@Component
|
|
||||||
export default class TheFooter extends StatefulVue {
|
|
||||||
private repositoryUrl: string = '';
|
|
||||||
private feedbackUrl: string = '';
|
|
||||||
|
|
||||||
public async mounted() {
|
|
||||||
const state = await this.getCurrentStateAsync();
|
|
||||||
this.repositoryUrl = state.app.repositoryUrl;
|
|
||||||
this.feedbackUrl = `${state.app.repositoryUrl}/issues`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
.privacy-policy {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
font-family: $normal-font;
|
|
||||||
text-align:center;
|
|
||||||
|
|
||||||
.line {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top:0.2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color:inherit;
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
$white: #fff;
|
$white: #fff;
|
||||||
$light-gray: #eceef1;
|
$light-gray: #eceef1;
|
||||||
$gray: darken(#eceef1, 30%);
|
$gray: darken($light-gray, 30%);
|
||||||
$dark-gray: #616f86;
|
$dark-gray: #616f86;
|
||||||
$slate: darken(#eceef1, 70%);
|
$slate: darken($light-gray, 70%);
|
||||||
$dark-slate: #2f3133;
|
$dark-slate: #2f3133;
|
||||||
$accent: #1abc9c;
|
$accent: #1abc9c;
|
||||||
$black: #000
|
$black: #000
|
||||||
3
src/presentation/styles/media.scss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
$big-screen-width: 992px;
|
||||||
|
$medium-screen-width: 768px;
|
||||||
|
$small-screen-width: 380px;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { expect } from 'chai';
|
||||||
|
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||||
|
import { BrowserOsDetector } from '@/application/Environment/BrowserOs/BrowserOsDetector';
|
||||||
|
import { BrowserOsTestCases } from './BrowserOsTestCases';
|
||||||
|
|
||||||
|
describe('BrowserOsDetector', () => {
|
||||||
|
it('unkown when user agent is undefined', () => {
|
||||||
|
// arrange
|
||||||
|
const sut = new BrowserOsDetector();
|
||||||
|
// act
|
||||||
|
const actual = sut.detect(undefined);
|
||||||
|
// assert
|
||||||
|
expect(actual).to.equal(OperatingSystem.Unknown);
|
||||||
|
});
|
||||||
|
it('detects as expected', () => {
|
||||||
|
for (const testCase of BrowserOsTestCases) {
|
||||||
|
// arrange
|
||||||
|
const sut = new BrowserOsDetector();
|
||||||
|
// act
|
||||||
|
const actual = sut.detect(testCase.userAgent);
|
||||||
|
// assert
|
||||||
|
expect(actual).to.equal(testCase.expectedOs,
|
||||||
|
`Expected: "${OperatingSystem[testCase.expectedOs]}"\n` +
|
||||||
|
`Actual: "${OperatingSystem[actual]}"\n` +
|
||||||
|
`UserAgent: "${testCase.userAgent}"`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||||
|
|
||||||
|
interface IBrowserOsTestCase {
|
||||||
|
userAgent: string;
|
||||||
|
expectedOs: OperatingSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BrowserOsTestCases: ReadonlyArray<IBrowserOsTestCase> = [
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 6.3; Win64, x64; Trident/7.0; rv:11.0) like Gecko',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/14.14316',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows Phone 10.0; Android 5.1.1; NOKIA; Lumia 1520) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586',
|
||||||
|
expectedOs: OperatingSystem.WindowsPhone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (X11; CrOS x86_64 11316.165.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.122 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.ChromeOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (X11; CrOS x86_64 8872.76.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.105 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.ChromeOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (X11; CrOS armv7l 4537.56.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.38 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.ChromeOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.3 Safari/605.1.15',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 OPR/58.0.3135.114',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36 OPR/53.0.2907.68',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2393.94 Safari/537.36 OPR/42.0.2393.94',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.82 Safari/537.36 OPR/29.0.1795.41 (Edition beta)',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 OPR/15.0.1147.100',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.10',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Opera/9.27 (Windows NT 5.1; U; en)',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1',
|
||||||
|
expectedOs: OperatingSystem.iOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
|
||||||
|
expectedOs: OperatingSystem.iOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A300 Safari/602.1',
|
||||||
|
expectedOs: OperatingSystem.iOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
|
||||||
|
expectedOs: OperatingSystem.iOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Opera/9.80 (Android; Opera Mini/32.0/88.150; U; sr) Presto/2.12 Version/12.16',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Opera/9.80 (Android; Opera Mini/8.0.1807/36.1609; U; en) Presto/2.12.423 Version/12.16',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.4; pt-br; SM-G530BT Build/KTU84P) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; zh-cn; Q40; Android/4.4.2; Release/12.15.2015) AppleWebKit/534.30 (KHTML, like Gecko) Mobile Safari/534.30',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.1429 Mobile Safari/537.10+',
|
||||||
|
expectedOs: OperatingSystem.BlackBerry,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.0.0; en-US) AppleWebKit/535.8+ (KHTML, like Gecko) Version/7.2.0.0 Safari/535.8+',
|
||||||
|
expectedOs: OperatingSystem.BlackBerryTabletOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.466 Mobile Safari/534.8+',
|
||||||
|
expectedOs: OperatingSystem.BlackBerryOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 4.4.4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 Mobile OPR/15.0.1147.100',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 2.3.4; MT11i Build/4.0.2.A.0.62) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.123 Mobile Safari/537.22 OPR/14.0.1025.52315',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Opera/9.80 (Android 2.2; Opera Mobi/-2118645896; U; pl) Presto/2.7.60 Version/10.5',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 9; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 6.0; CAM-L03) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 4.3; Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Android 9; Mobile; rv:64.0) Gecko/64.0 Firefox/64.0',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4',
|
||||||
|
expectedOs: OperatingSystem.iOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 625) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537',
|
||||||
|
expectedOs: OperatingSystem.WindowsPhone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)',
|
||||||
|
expectedOs: OperatingSystem.WindowsPhone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 6.0; en-US; CPH1609 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.2.1164 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'UCWEB/2.0 (Linux; U; Adr 5.1; en-US; Lenovo Z90a40 Build/LMY47O) U2/1.0.0 UCBrowser/11.1.5.890 U2/1.0.0 Mobile',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 5.1; en-US; Lenovo Z90a40 Build/LMY47O) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/11.1.5.890 U3/0.8.0 Mobile Safari/534.30',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'UCWEB/2.0 (Linux; U; Adr 2.3; en-US; MI-ONEPlus) U2/1.0.0 UCBrowser/8.6.0.199 U2/1.0.0 Mobile',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 2.3; zh-CN; MI-ONEPlus) AppleWebKit/534.13 (KHTML, like Gecko) UCBrowser/8.6.0.199 U3/0.8.0 Mobile Safari/534.13',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 9; SAMSUNG SM-G965F Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.0 Chrome/67.0.3396.87 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; SAMSUNG SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/8.2 Chrome/63.0.3239.111 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-J330FN Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/7.2 Chrome/59.0.3071.125 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 8.1.0; zh-cn; vivo X21A Build/OPM1.171019.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/9.1 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; zh-cn; GT-I9500 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.0 QQ-URL-Manager Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Linux; Android 9; ONEPLUS A6003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Mobile Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Android,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 6.4; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1',
|
||||||
|
expectedOs: OperatingSystem.iOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0',
|
||||||
|
expectedOs: OperatingSystem.Linux,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (X11; CrOS x86_64 11316.165.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.122 Safari/537.36',
|
||||||
|
expectedOs: OperatingSystem.ChromeOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userAgent: 'Mozilla/5.0 (Mobile; LYF/F90M/LYF_F90M_000-03-12-110119; Android; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5',
|
||||||
|
expectedOs: OperatingSystem.KaiOS,
|
||||||
|
},
|
||||||
|
];
|
||||||
38
tests/unit/application/Environment/DesktopOsTestCases.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||||
|
|
||||||
|
interface IDesktopTestCase {
|
||||||
|
processPlatform: string;
|
||||||
|
expectedOs: OperatingSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://nodejs.org/api/process.html#process_process_platform
|
||||||
|
export const DesktopOsTestCases: ReadonlyArray<IDesktopTestCase> = [
|
||||||
|
{
|
||||||
|
processPlatform: 'aix',
|
||||||
|
expectedOs: OperatingSystem.Unknown,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processPlatform: 'darwin',
|
||||||
|
expectedOs: OperatingSystem.macOS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processPlatform: 'freebsd',
|
||||||
|
expectedOs: OperatingSystem.Unknown,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processPlatform: 'linux',
|
||||||
|
expectedOs: OperatingSystem.Linux,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processPlatform: 'openbsd',
|
||||||
|
expectedOs: OperatingSystem.Unknown,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processPlatform: 'sunos',
|
||||||
|
expectedOs: OperatingSystem.Unknown,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processPlatform: 'win32',
|
||||||
|
expectedOs: OperatingSystem.Windows,
|
||||||
|
},
|
||||||
|
];
|
||||||
103
tests/unit/application/Environment/Environment.spec.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { IBrowserOsDetector } from '@/application/Environment/BrowserOs/IBrowserOsDetector';
|
||||||
|
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||||
|
import { DesktopOsTestCases } from './DesktopOsTestCases';
|
||||||
|
import { Environment } from '@/application/Environment/Environment';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
|
||||||
|
interface EnvironmentVariables {
|
||||||
|
window?: any;
|
||||||
|
process?: any;
|
||||||
|
navigator?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SystemUnderTest extends Environment {
|
||||||
|
constructor(variables: EnvironmentVariables, browserOsDetector?: IBrowserOsDetector) {
|
||||||
|
super(variables as any, browserOsDetector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Environment', () => {
|
||||||
|
describe('isDesktop', () => {
|
||||||
|
it('returns true if process type is renderer', () => {
|
||||||
|
// arrange
|
||||||
|
const window = {
|
||||||
|
process: {
|
||||||
|
type: 'renderer',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// act
|
||||||
|
const sut = new SystemUnderTest({ window });
|
||||||
|
// assert
|
||||||
|
expect(sut.isDesktop).to.equal(true);
|
||||||
|
});
|
||||||
|
it('returns true if electron is defined as process version', () => {
|
||||||
|
// arrange
|
||||||
|
const process = {
|
||||||
|
versions: {
|
||||||
|
electron: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// act
|
||||||
|
const sut = new SystemUnderTest({ process });
|
||||||
|
// assert
|
||||||
|
expect(sut.isDesktop).to.equal(true);
|
||||||
|
});
|
||||||
|
it('returns true if navigator user agent has electron', () => {
|
||||||
|
// arrange
|
||||||
|
const navigator = {
|
||||||
|
userAgent: 'Electron',
|
||||||
|
};
|
||||||
|
// act
|
||||||
|
const sut = new SystemUnderTest( { navigator });
|
||||||
|
// assert
|
||||||
|
expect(sut.isDesktop).to.equal(true);
|
||||||
|
});
|
||||||
|
it('returns false as default', () => {
|
||||||
|
const sut = new SystemUnderTest({ });
|
||||||
|
expect(sut.isDesktop).to.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('os', () => {
|
||||||
|
describe('browser os from BrowserOsDetector', () => {
|
||||||
|
// arrange
|
||||||
|
const givenUserAgent = 'testUserAgent';
|
||||||
|
const expected = OperatingSystem.macOS;
|
||||||
|
const window = {
|
||||||
|
navigator: {
|
||||||
|
userAgent: givenUserAgent,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mock: IBrowserOsDetector = {
|
||||||
|
detect: (agent) => {
|
||||||
|
if (agent !== givenUserAgent) {
|
||||||
|
throw new Error('Unexpected user agent');
|
||||||
|
}
|
||||||
|
return expected;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// act
|
||||||
|
const sut = new SystemUnderTest({ window }, mock);
|
||||||
|
const actual = sut.os;
|
||||||
|
// assert
|
||||||
|
expect(actual).to.equal(expected);
|
||||||
|
});
|
||||||
|
describe('desktop os', () => {
|
||||||
|
const navigator = {
|
||||||
|
userAgent: 'Electron',
|
||||||
|
};
|
||||||
|
for (const testCase of DesktopOsTestCases) {
|
||||||
|
// arrange
|
||||||
|
const process = {
|
||||||
|
platform: testCase.processPlatform,
|
||||||
|
};
|
||||||
|
// act
|
||||||
|
const sut = new SystemUnderTest({ navigator, process });
|
||||||
|
// assert
|
||||||
|
expect(sut.os).to.equal(testCase.expectedOs,
|
||||||
|
`Expected: "${OperatingSystem[testCase.expectedOs]}"\n` +
|
||||||
|
`Actual: "${OperatingSystem[sut.os]}"\n` +
|
||||||
|
`Platform: "${testCase.processPlatform}"`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Signal } from '@/infrastructure/Events/Signal';
|
import { Signal } from '@/infrastructure/Events/Signal';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
describe('Signal Tests', () => {
|
describe('Signal', () => {
|
||||||
class ReceiverMock {
|
class ReceiverMock {
|
||||||
public onRecieveCalls = new Array<number>();
|
public onRecieveCalls = new Array<number>();
|
||||||
public onReceive(arg: number): void { this.onRecieveCalls.push(arg); }
|
public onReceive(arg: number): void { this.onRecieveCalls.push(arg); }
|
||||||
|
|||||||
@@ -1 +1,22 @@
|
|||||||
process.env.VUE_APP_VERSION = require('./package.json').version;
|
process.env.VUE_APP_VERSION = require('./package.json').version;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
pluginOptions: {
|
||||||
|
// https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/guide.html#native-modules
|
||||||
|
electronBuilder: {
|
||||||
|
// https://www.electron.build/configuration/configuration
|
||||||
|
builderOptions: {
|
||||||
|
win: {
|
||||||
|
icon: './public/favicon.ico'
|
||||||
|
},
|
||||||
|
publish: [{
|
||||||
|
// https://www.electron.build/configuration/publish#githuboptions
|
||||||
|
// https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#enable-publishing-to-github
|
||||||
|
provider: 'github',
|
||||||
|
vPrefixedTagName: false, // default: true
|
||||||
|
releaseType: 'release' // or "draft" (default), "prerelease"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||