Merge branch 'feature/more-eslint' into 'develop'

 More ESLint & Prettier

See merge request NatoBoram/public-gateway-cacher!9
develop
Nato Boram 2022-03-11 07:17:23 +00:00
commit 4115e3ee4b
53 changed files with 958 additions and 636 deletions

View File

@ -1,4 +1,3 @@
# Editor configuration, see https://editorconfig.org
root = true root = true
[*] [*]
@ -9,8 +8,6 @@ insert_final_newline = true
max_line_length = 140 max_line_length = 140
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md] [*.md]
max_line_length = false
trim_trailing_whitespace = false trim_trailing_whitespace = false

8
.eslintignore Normal file
View File

@ -0,0 +1,8 @@
.angular
.pnpm-store
dist
e2e/protractor.conf.js
node_modules
pnpm-lock.yaml
public
src/test.ts

View File

@ -1,22 +1,25 @@
{ {
"root": true, "root": true,
"ignorePatterns": [ "env": {
"projects/**/*" "browser": true
], },
"ignorePatterns": ["projects/**/*"],
"overrides": [ "overrides": [
{ {
"files": [ "files": ["*.ts"],
"*.ts"
],
"parserOptions": { "parserOptions": {
"project": [ "project": ["tsconfig.json", "e2e/tsconfig.json"],
"tsconfig.json",
"e2e/tsconfig.json"
],
"createDefaultProgram": true "createDefaultProgram": true
}, },
"plugins": ["rxjs-angular"],
"extends": [ "extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
"plugin:rxjs/recommended",
"plugin:@angular-eslint/all", "plugin:@angular-eslint/all",
"plugin:@angular-eslint/recommended--extra",
"plugin:@angular-eslint/template/process-inline-templates" "plugin:@angular-eslint/template/process-inline-templates"
], ],
"rules": { "rules": {
@ -35,17 +38,93 @@
"prefix": "app", "prefix": "app",
"style": "kebab-case" "style": "kebab-case"
} }
] ],
// Custom rules // Custom rules
// @typescript-eslint
"@typescript-eslint/array-type": "error",
"@typescript-eslint/class-literal-property-style": "error",
"@typescript-eslint/consistent-indexed-object-style": "error",
"@typescript-eslint/consistent-type-assertions": [
"error",
{
"assertionStyle": "as",
"objectLiteralTypeAssertions": "never"
}
],
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/dot-notation": "error",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"accessibility": "no-public"
}
],
"@typescript-eslint/member-delimiter-style": [
"error",
{
"multiline": {
"delimiter": "none",
"requireLast": true
}
}
],
"@typescript-eslint/method-signature-style": "error",
"@typescript-eslint/no-base-to-string": "error",
"@typescript-eslint/no-confusing-non-null-assertion": "error",
"@typescript-eslint/no-confusing-void-expression": [
"error",
{
"ignoreArrowShorthand": true,
"ignoreVoidOperator": true
}
],
"@typescript-eslint/no-dynamic-delete": "error",
"@typescript-eslint/no-empty-function": [
"error",
{
"allow": ["private-constructors", "protected-constructors"]
}
],
"@typescript-eslint/no-extra-parens": ["error", "functions"],
"@typescript-eslint/no-implicit-any-catch": "error",
"@typescript-eslint/no-invalid-void-type": "error",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
"@typescript-eslint/no-unnecessary-condition": "error",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-arguments": "error",
"@typescript-eslint/no-unnecessary-type-constraint": "error",
"@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/non-nullable-type-assertion-style": "error",
"@typescript-eslint/prefer-for-of": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/prefer-optional-chain": "error",
"@typescript-eslint/prefer-readonly": "error",
"@typescript-eslint/prefer-reduce-type-parameter": "error",
"@typescript-eslint/prefer-return-this-type": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/prefer-ts-expect-error": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/sort-type-union-intersection-members": "error",
"@typescript-eslint/switch-exhaustiveness-check": "error",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unified-signatures": "error",
// eslint-plugin-rxjs-angular
//"rxjs-angular/prefer-async-pipe": "error",
"rxjs-angular/prefer-takeuntil": "error"
} }
}, },
{ {
"files": [ "files": ["*.html"],
"*.html" "extends": ["plugin:@angular-eslint/template/all"],
],
"extends": [
"plugin:@angular-eslint/template/all"
],
"rules": { "rules": {
// Custom rules // Custom rules
"@angular-eslint/template/i18n": "off" "@angular-eslint/template/i18n": "off"

143
.gitignore vendored
View File

@ -1,47 +1,126 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # Created by https://www.toptal.com/developers/gitignore/api/angular,linux,windows,macos,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=angular,linux,windows,macos,visualstudiocode
### Angular ###
## Angular ##
# compiled output # compiled output
/dist dist/
/tmp tmp/
/out-tsc app/**/*.js
# Only exists if Bazel was run app/**/*.js.map
/bazel-out
# dependencies # dependencies
/node_modules node_modules/
bower_components/
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors # IDEs and editors
/.idea .idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode # misc
.sass-cache/
connect.lock/
coverage/
libpeerconnection.log/
npm-debug.log
testem.log
typings/
.angular/
# e2e
e2e/*.js
e2e/*.map
# System Files
.DS_Store/
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### VisualStudioCode ###
.vscode/* .vscode/*
!.vscode/settings.json !.vscode/settings.json
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
.history/* !.vscode/*.code-snippets
# misc # Local History for Visual Studio Code
/.angular/cache .history/
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files # Built Visual Studio Code Extensions
.DS_Store *.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# Support for Project snippet scope
### Windows ###
# Windows thumbnail cache files
Thumbs.db Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/angular,linux,windows,macos,visualstudiocode

View File

@ -27,7 +27,8 @@ build:
test: test:
stage: test stage: test
script: script:
- pnpm run lint - pnpx ng lint
- pnpx prettier --check .
pages: pages:
stage: deploy stage: deploy

6
.prettierignore Normal file
View File

@ -0,0 +1,6 @@
.angular
.pnpm-store
dist
node_modules
pnpm-lock.yaml
public

3
.prettierrc.yaml Normal file
View File

@ -0,0 +1,3 @@
arrowParens: avoid
printWidth: 140
semi: false

View File

@ -7,10 +7,11 @@
"davidanson.vscode-markdownlint", "davidanson.vscode-markdownlint",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"editorconfig.editorconfig", "editorconfig.editorconfig",
"esbenp.prettier-vscode",
"gitlab.gitlab-workflow", "gitlab.gitlab-workflow",
"johnpapa.angular2", "johnpapa.angular2",
"ms-vscode.vscode-typescript-next", "ms-vscode.vscode-typescript-next",
"visualstudioexptteam.vscodeintellicode", "visualstudioexptteam.vscodeintellicode"
], ],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace. // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [ "unwantedRecommendations": [

4
.vscode/launch.json vendored
View File

@ -37,9 +37,7 @@
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/node_modules/protractor/bin/protractor", "program": "${workspaceFolder}/node_modules/protractor/bin/protractor",
"protocol": "inspector", "protocol": "inspector",
"args": [ "args": ["${workspaceFolder}/e2e/protractor.conf.js"]
"${workspaceFolder}/e2e/protractor.conf.js"
]
} }
] ]
} }

21
.vscode/settings.json vendored
View File

@ -1,27 +1,20 @@
{ {
"[css][scss][html][javascript][typescript][json][jsonc][markdown][yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": true, "source.fixAll": true,
"source.fixAll.eslint": true, "source.fixAll.eslint": true,
"source.organizeImports": true "source.organizeImports": true
}, },
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.rulers": [ "editor.rulers": [140],
140
],
"eslint.options": { "eslint.options": {
"extensions": [ "extensions": [".ts", ".html"]
".ts",
".html"
]
}, },
"eslint.packageManager": "pnpm", "eslint.packageManager": "pnpm",
"eslint.validate": [ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "html"],
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html"
],
"files.associations": { "files.associations": {
".ipfs-npmrc": "json" ".ipfs-npmrc": "json"
}, },

7
.vscode/tasks.json vendored
View File

@ -17,10 +17,7 @@
"owner": "typescript", "owner": "typescript",
"source": "ts", "source": "ts",
"applyTo": "closedDocuments", "applyTo": "closedDocuments",
"fileLocation": [ "fileLocation": ["relative", "${cwd}"],
"relative",
"${cwd}"
],
"pattern": "$tsc", "pattern": "$tsc",
"background": { "background": {
"activeOnStart": true, "activeOnStart": true,
@ -32,6 +29,6 @@
} }
} }
} }
}, }
] ]
} }

View File

@ -8,20 +8,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
### Added ### Added
* More information in the `package.json`. - More information in the `package.json`.
* Added ESLint - Added ESLint
### Changed ### Changed
* Updated `gateways.json` - Updated `gateways.json`
* Upgraded to Angular 13 - Upgraded to Angular 13
* Targets ESNext - Targets ESNext
### Deprecated ### Deprecated
### Removed ### Removed
* Removed TSLint - Removed TSLint
### Fixed ### Fixed
@ -31,33 +31,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
### Added ### Added
* Added some accessibility tags - Added some accessibility tags
* Added some VSCode settings - Added some VSCode settings
### Changed ### Changed
* Updated to Angular 11 - Updated to Angular 11
* Updated gateways - Updated gateways
### Fixed ### Fixed
* Corrected the title from "Public IPFS Cacher" to "Public Gateway Cacher". - Corrected the title from "Public IPFS Cacher" to "Public Gateway Cacher".
## [2.1.0] - 2020-07-10 ## [2.1.0] - 2020-07-10
### Added ### Added
* Support for subdomain gateways - Support for subdomain gateways
* Strict mode - Strict mode
* More icons - More icons
* IPFS colours - IPFS colours
### Changed ### Changed
* Better screenshot - Better screenshot
* Upgraded to Angular 10 - Upgraded to Angular 10
* Now uses `--chunker=buzhash` - Now uses `--chunker=buzhash`
* Updated `bootstrap` and `ipfs-css` - Updated `bootstrap` and `ipfs-css`
## [2.0.0] - 2019-12-03 ## [2.0.0] - 2019-12-03
@ -67,7 +67,7 @@ Tests were done with `ipfs-npm` : They weren't successful. `ipfs-css` is only mi
### Added ### Added
* GitLab Pages - <https://natoboram.gitlab.io/public-gateway-cacher/> - GitLab Pages - <https://natoboram.gitlab.io/public-gateway-cacher/>
## [1.0.0] - 2019-11-28 ## [1.0.0] - 2019-11-28
@ -75,9 +75,9 @@ This version is a fork of [github.com/ipfs/public-gateway-checker](https://githu
## Types of changes ## Types of changes
* `Added` for new features. - `Added` for new features.
* `Changed` for changes in existing functionality. - `Changed` for changes in existing functionality.
* `Deprecated` for soon-to-be removed features. - `Deprecated` for soon-to-be removed features.
* `Removed` for now removed features. - `Removed` for now removed features.
* `Fixed` for any bug fixes. - `Fixed` for any bug fixes.
* `Security` in case of vulnerabilities. - `Security` in case of vulnerabilities.

View File

@ -217,23 +217,23 @@ produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these terms of section 4, provided that you also meet all of these
conditions: conditions:
- a) The work must carry prominent notices stating that you modified - a) The work must carry prominent notices stating that you modified
it, and giving a relevant date. it, and giving a relevant date.
- b) The work must carry prominent notices stating that it is - b) The work must carry prominent notices stating that it is
released under this License and any conditions added under released under this License and any conditions added under
section 7. This requirement modifies the requirement in section 4 section 7. This requirement modifies the requirement in section 4
to "keep intact all notices". to "keep intact all notices".
- c) You must license the entire work, as a whole, under this - c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7 License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts, additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it. invalidate such permission if you have separately received it.
- d) If the work has interactive user interfaces, each must display - d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your interfaces that do not display Appropriate Legal Notices, your
work need not make them do so. work need not make them do so.
A compilation of a covered work with other separate and independent A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work, works, which are not by their nature extensions of the covered work,
@ -252,42 +252,42 @@ sections 4 and 5, provided that you also convey the machine-readable
Corresponding Source under the terms of this License, in one of these Corresponding Source under the terms of this License, in one of these
ways: ways:
- a) Convey the object code in, or embodied in, a physical product - a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the (including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium Corresponding Source fixed on a durable physical medium
customarily used for software interchange. customarily used for software interchange.
- b) Convey the object code in, or embodied in, a physical product - b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a (including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the Corresponding conveying of source, or (2) access to copy the Corresponding
Source from a network server at no charge. Source from a network server at no charge.
- c) Convey individual copies of the object code with a copy of the - c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord only if you received the object code with such an offer, in accord
with subsection 6b. with subsection 6b.
- d) Convey the object code by offering access from a designated - d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party) may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements. available for as long as needed to satisfy these requirements.
- e) Convey the object code using peer-to-peer transmission, - e) Convey the object code using peer-to-peer transmission,
provided you inform other peers where the object code and provided you inform other peers where the object code and
Corresponding Source of the work are being offered to the general Corresponding Source of the work are being offered to the general
public at no charge under subsection 6d. public at no charge under subsection 6d.
A separable portion of the object code, whose source code is excluded A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be from the Corresponding Source as a System Library, need not be
@ -363,23 +363,23 @@ Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders add to a covered work, you may (if authorized by the copyright holders
of that material) supplement the terms of this License with terms: of that material) supplement the terms of this License with terms:
- a) Disclaiming warranty or limiting liability differently from the - a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or terms of sections 15 and 16 of this License; or
- b) Requiring preservation of specified reasonable legal notices or - b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or Notices displayed by works containing it; or
- c) Prohibiting misrepresentation of the origin of that material, - c) Prohibiting misrepresentation of the origin of that material,
or requiring that modified versions of such material be marked in or requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or reasonable ways as different from the original version; or
- d) Limiting the use for publicity purposes of names of licensors - d) Limiting the use for publicity purposes of names of licensors
or authors of the material; or or authors of the material; or
- e) Declining to grant rights under trademark law for use of some - e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or trade names, trademarks, or service marks; or
- f) Requiring indemnification of licensors and authors of that - f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions material by anyone who conveys the material (or modified versions
of it) with contractual assumptions of liability to the recipient, of it) with contractual assumptions of liability to the recipient,
for any liability that these contractual assumptions directly for any liability that these contractual assumptions directly
impose on those licensors and authors. impose on those licensors and authors.
All other non-permissive additional terms are considered "further All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you restrictions" within the meaning of section 10. If the Program as you

View File

@ -30,17 +30,10 @@
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"assets": [ "assets": ["src/favicon.png", "src/assets"],
"src/favicon.png", "styles": ["src/styles.scss"],
"src/assets"
],
"styles": [
"src/styles.scss"
],
"stylePreprocessorOptions": { "stylePreprocessorOptions": {
"includePaths": [ "includePaths": ["node_modules"]
"node_modules"
]
}, },
"scripts": [], "scripts": [],
"vendorChunk": true, "vendorChunk": true,
@ -150,17 +143,10 @@
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js", "karmaConfig": "karma.conf.js",
"assets": [ "assets": ["src/favicon.png", "src/assets"],
"src/favicon.png", "styles": ["src/styles.scss"],
"src/assets"
],
"styles": [
"src/styles.scss"
],
"stylePreprocessorOptions": { "stylePreprocessorOptions": {
"includePaths": [ "includePaths": ["node_modules"]
"node_modules"
]
}, },
"scripts": [] "scripts": []
} }
@ -180,10 +166,7 @@
"lint": { "lint": {
"builder": "@angular-eslint/builder:lint", "builder": "@angular-eslint/builder:lint",
"options": { "options": {
"lintFilePatterns": [ "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
"src/**/*.ts",
"src/**/*.html"
]
} }
} }
} }

View File

@ -2,35 +2,35 @@
// Protractor configuration file, see link for more information // Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts // https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); const { SpecReporter, StacktraceOption } = require("jasmine-spec-reporter")
/** /**
* @type { import("protractor").Config } * @type { import("protractor").Config }
*/ */
exports.config = { exports.config = {
allScriptsTimeout: 11000, allScriptsTimeout: 11000,
specs: [ specs: ["./src/**/*.e2e-spec.ts"],
'./src/**/*.e2e-spec.ts'
],
capabilities: { capabilities: {
browserName: 'chrome' browserName: "chrome",
}, },
directConnect: true, directConnect: true,
baseUrl: 'http://localhost:4200/', baseUrl: "http://localhost:4200/",
framework: 'jasmine', framework: "jasmine",
jasmineNodeOpts: { jasmineNodeOpts: {
showColors: true, showColors: true,
defaultTimeoutInterval: 30000, defaultTimeoutInterval: 30000,
print: function () { } print: function () {},
}, },
onPrepare() { onPrepare() {
require('ts-node').register({ require("ts-node").register({
project: require('path').join(__dirname, './tsconfig.json') project: require("path").join(__dirname, "./tsconfig.json"),
}); })
jasmine.getEnv().addReporter(new SpecReporter({ jasmine.getEnv().addReporter(
spec: { new SpecReporter({
displayStacktrace: StacktraceOption.PRETTY spec: {
} displayStacktrace: StacktraceOption.PRETTY,
})); },
} })
}; )
},
}

View File

@ -1,23 +1,25 @@
import { browser, logging } from 'protractor'; import { browser, logging } from "protractor"
import { AppPage } from './app.po'; import { AppPage } from "./app.po"
describe('workspace-project App', (): void => { describe("workspace-project App", (): void => {
let page: AppPage; let page: AppPage
beforeEach((): void => { beforeEach((): void => {
page = new AppPage(); page = new AppPage()
}); })
it('should display welcome message', (): void => { it("should display welcome message", (): void => {
page.navigateTo(); void page.navigateTo()
expect(page.getTitleText()).toEqual('public-gateway-cacher app is running!'); void expect(page.getTitleText()).toEqual("public-gateway-cacher app is running!")
}); })
afterEach(async (): Promise<void> => { afterEach(async (): Promise<void> => {
// Assert that there are no errors emitted from the browser // Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER); const logs = await browser.manage().logs().get(logging.Type.BROWSER)
expect(logs).not.toContain(jasmine.objectContaining({ const expected: Partial<logging.Entry> = {
level: logging.Level.SEVERE, level: logging.Level.SEVERE,
} as logging.Entry)); }
});
}); void expect(logs).not.toContain(jasmine.objectContaining(expected))
})
})

View File

@ -1,11 +1,11 @@
import { browser, by, element } from 'protractor'; import { browser, by, element } from "protractor"
export class AppPage { export class AppPage {
navigateTo(): Promise<unknown> { async navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>; return browser.get(browser.baseUrl) as Promise<unknown>
} }
getTitleText(): Promise<string> { async getTitleText(): Promise<string> {
return element(by.css('pgc-root .content span')).getText() as Promise<string>; return element(by.css("pgc-root .content span")).getText() as Promise<string>
} }
} }

View File

@ -5,10 +5,6 @@
"outDir": "../out-tsc/e2e", "outDir": "../out-tsc/e2e",
"module": "commonjs", "module": "commonjs",
"target": "ESNext", "target": "ESNext",
"types": [ "types": ["jasmine", "jasminewd2", "node"]
"jasmine",
"jasminewd2",
"node"
]
} }
} }

View File

@ -3,30 +3,30 @@
module.exports = function (config) { module.exports = function (config) {
config.set({ config.set({
basePath: '', basePath: "",
frameworks: ['jasmine', '@angular-devkit/build-angular'], frameworks: ["jasmine", "@angular-devkit/build-angular"],
plugins: [ plugins: [
require('karma-jasmine'), require("karma-jasmine"),
require('karma-chrome-launcher'), require("karma-chrome-launcher"),
require('karma-jasmine-html-reporter'), require("karma-jasmine-html-reporter"),
require('karma-coverage-istanbul-reporter'), require("karma-coverage-istanbul-reporter"),
require('@angular-devkit/build-angular/plugins/karma') require("@angular-devkit/build-angular/plugins/karma"),
], ],
client: { client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser clearContext: false, // leave Jasmine Spec Runner output visible in browser
}, },
coverageIstanbulReporter: { coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/public-gateway-cacher'), dir: require("path").join(__dirname, "./coverage/public-gateway-cacher"),
reports: ['html', 'lcovonly', 'text-summary'], reports: ["html", "lcovonly", "text-summary"],
fixWebpackSourcePaths: true fixWebpackSourcePaths: true,
}, },
reporters: ['progress', 'kjhtml'], reporters: ["progress", "kjhtml"],
port: 9876, port: 9876,
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
browsers: ['Chrome'], browsers: ["Chrome"],
singleRun: false, singleRun: false,
restartOnFileChange: true restartOnFileChange: true,
}); })
}; }

View File

@ -31,7 +31,9 @@
"build:gitlab": "ng build --configuration=gitlab --base-href /public-gateway-cacher/", "build:gitlab": "ng build --configuration=gitlab --base-href /public-gateway-cacher/",
"build:ipfs": "ng build --configuration=ipfs", "build:ipfs": "ng build --configuration=ipfs",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint --fix",
"eslint": "eslint --fix .",
"prettier": "prettier --write .",
"e2e": "ng e2e", "e2e": "ng e2e",
"publish:ipfs": "pnpm run build:ipfs && ipfs add --recursive --chunker=buzhash --cid-version=1 dist/angular", "publish:ipfs": "pnpm run build:ipfs && ipfs add --recursive --chunker=buzhash --cid-version=1 dist/angular",
"postinstall": "ngcc" "postinstall": "ngcc"
@ -70,6 +72,9 @@
"@typescript-eslint/eslint-plugin": "5.14.0", "@typescript-eslint/eslint-plugin": "5.14.0",
"@typescript-eslint/parser": "5.14.0", "@typescript-eslint/parser": "5.14.0",
"eslint": "^8.2.0", "eslint": "^8.2.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-rxjs": "^5.0.2",
"eslint-plugin-rxjs-angular": "^2.0.0",
"jasmine-core": "~4.0.1", "jasmine-core": "~4.0.1",
"jasmine-spec-reporter": "~7.0.0", "jasmine-spec-reporter": "~7.0.0",
"karma": "~6.3.17", "karma": "~6.3.17",
@ -77,6 +82,7 @@
"karma-coverage-istanbul-reporter": "~3.0.2", "karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0", "karma-jasmine-html-reporter": "^1.5.0",
"prettier": "^2.5.1",
"protractor": "~7.0.0", "protractor": "~7.0.0",
"ts-node": "~10.7.0", "ts-node": "~10.7.0",
"typescript": "~4.5.5" "typescript": "~4.5.5"

View File

@ -28,6 +28,9 @@ specifiers:
'@typescript-eslint/parser': 5.14.0 '@typescript-eslint/parser': 5.14.0
bootstrap: ^5.1.3 bootstrap: ^5.1.3
eslint: ^8.2.0 eslint: ^8.2.0
eslint-config-prettier: ^8.5.0
eslint-plugin-rxjs: ^5.0.2
eslint-plugin-rxjs-angular: ^2.0.0
ipfs-css: ^1.3.0 ipfs-css: ^1.3.0
jasmine-core: ~4.0.1 jasmine-core: ~4.0.1
jasmine-spec-reporter: ~7.0.0 jasmine-spec-reporter: ~7.0.0
@ -36,6 +39,7 @@ specifiers:
karma-coverage-istanbul-reporter: ~3.0.2 karma-coverage-istanbul-reporter: ~3.0.2
karma-jasmine: ~4.0.0 karma-jasmine: ~4.0.0
karma-jasmine-html-reporter: ^1.5.0 karma-jasmine-html-reporter: ^1.5.0
prettier: ^2.5.1
protractor: ~7.0.0 protractor: ~7.0.0
rxjs: ~7.5.5 rxjs: ~7.5.5
ts-node: ~10.7.0 ts-node: ~10.7.0
@ -77,6 +81,9 @@ devDependencies:
'@typescript-eslint/eslint-plugin': 5.14.0_e74fb90c35f99587899145b1078aa5b4 '@typescript-eslint/eslint-plugin': 5.14.0_e74fb90c35f99587899145b1078aa5b4
'@typescript-eslint/parser': 5.14.0_eslint@8.10.0+typescript@4.5.5 '@typescript-eslint/parser': 5.14.0_eslint@8.10.0+typescript@4.5.5
eslint: 8.10.0 eslint: 8.10.0
eslint-config-prettier: 8.5.0_eslint@8.10.0
eslint-plugin-rxjs: 5.0.2_eslint@8.10.0+typescript@4.5.5
eslint-plugin-rxjs-angular: 2.0.0_eslint@8.10.0+typescript@4.5.5
jasmine-core: 4.0.1 jasmine-core: 4.0.1
jasmine-spec-reporter: 7.0.0 jasmine-spec-reporter: 7.0.0
karma: 6.3.17 karma: 6.3.17
@ -84,6 +91,7 @@ devDependencies:
karma-coverage-istanbul-reporter: 3.0.3 karma-coverage-istanbul-reporter: 3.0.3
karma-jasmine: 4.0.1_karma@6.3.17 karma-jasmine: 4.0.1_karma@6.3.17
karma-jasmine-html-reporter: 1.7.0_d63185f8de84f984ad7c6427ff04984c karma-jasmine-html-reporter: 1.7.0_d63185f8de84f984ad7c6427ff04984c
prettier: 2.5.1
protractor: 7.0.0 protractor: 7.0.0
ts-node: 10.7.0_99a448058f874aec2a353d0e974167cc ts-node: 10.7.0_99a448058f874aec2a353d0e974167cc
typescript: 4.5.5 typescript: 4.5.5
@ -600,7 +608,7 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/code-frame': 7.16.7 '@babel/code-frame': 7.16.7
'@babel/generator': 7.16.8 '@babel/generator': 7.17.3
'@babel/helper-compilation-targets': 7.16.7_@babel+core@7.16.12 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.16.12
'@babel/helper-module-transforms': 7.17.6 '@babel/helper-module-transforms': 7.17.6
'@babel/helpers': 7.17.2 '@babel/helpers': 7.17.2
@ -2074,7 +2082,7 @@ packages:
dependencies: dependencies:
'@swc-node/core': 1.8.2 '@swc-node/core': 1.8.2
'@swc-node/sourcemap-support': 0.1.11 '@swc-node/sourcemap-support': 0.1.11
chalk: 4.1.0 chalk: 4.1.2
debug: 4.3.3 debug: 4.3.3
pirates: 4.0.5 pirates: 4.0.5
tslib: 2.3.1 tslib: 2.3.1
@ -2406,6 +2414,16 @@ packages:
'@types/node': 17.0.21 '@types/node': 17.0.21
dev: true dev: true
/@types/yargs-parser/21.0.0:
resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
dev: true
/@types/yargs/17.0.9:
resolution: {integrity: sha512-Ci8+4/DOtkHRylcisKmVMtmVO5g7weUVCKcsu1sJvF1bn0wExTmbHmhFKj7AnEm0de800iovGhdSKzYnzbaHpg==}
dependencies:
'@types/yargs-parser': 21.0.0
dev: true
/@typescript-eslint/eslint-plugin/5.14.0_e74fb90c35f99587899145b1078aa5b4: /@typescript-eslint/eslint-plugin/5.14.0_e74fb90c35f99587899145b1078aa5b4:
resolution: {integrity: sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==} resolution: {integrity: sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -3153,6 +3171,14 @@ packages:
tweetnacl: 0.14.5 tweetnacl: 0.14.5
dev: true dev: true
/bent/7.3.12:
resolution: {integrity: sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==}
dependencies:
bytesish: 0.4.4
caseless: 0.12.0
is-stream: 2.0.1
dev: true
/big.js/5.2.2: /big.js/5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
dev: true dev: true
@ -3276,6 +3302,10 @@ packages:
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dev: true dev: true
/bytesish/0.4.4:
resolution: {integrity: sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ==}
dev: true
/cacache/15.3.0: /cacache/15.3.0:
resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -3497,6 +3527,11 @@ packages:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true dev: true
/common-tags/1.8.2:
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
engines: {node: '>=4.0.0'}
dev: true
/commondir/1.0.1: /commondir/1.0.1:
resolution: {integrity: sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=} resolution: {integrity: sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=}
dev: true dev: true
@ -3791,6 +3826,11 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/decamelize/5.0.1:
resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==}
engines: {node: '>=10'}
dev: true
/decode-uri-component/0.2.0: /decode-uri-component/0.2.0:
resolution: {integrity: sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=} resolution: {integrity: sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=}
engines: {node: '>=0.10'} engines: {node: '>=0.10'}
@ -4323,6 +4363,68 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: true dev: true
/eslint-config-prettier/8.5.0_eslint@8.10.0:
resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
eslint: 8.10.0
dev: true
/eslint-etc/5.1.0_eslint@8.10.0+typescript@4.5.5:
resolution: {integrity: sha512-Rmjl01h5smi5cbsFne2xpTuch2xNnwXiX2lbS4HttXUN5FwXKAwG1UEFBVGO1nC091YO/QyVahyfNPJSX2ae+g==}
peerDependencies:
eslint: ^8.0.0
typescript: ^4.0.0
dependencies:
'@typescript-eslint/experimental-utils': 5.11.0_eslint@8.10.0+typescript@4.5.5
eslint: 8.10.0
tsutils: 3.21.0_typescript@4.5.5
tsutils-etc: 1.4.1_tsutils@3.21.0+typescript@4.5.5
typescript: 4.5.5
transitivePeerDependencies:
- supports-color
dev: true
/eslint-plugin-rxjs-angular/2.0.0_eslint@8.10.0+typescript@4.5.5:
resolution: {integrity: sha512-MalcYcEHOK2NT+avWSI1PsUilwGx6cprMQdw9jJRlCTkIvsUvCGFD1eTqQKVImwkK8+te732v9VsP/XcXlKZqA==}
peerDependencies:
eslint: ^8.0.0
typescript: ^4.0.0
dependencies:
'@typescript-eslint/experimental-utils': 5.11.0_eslint@8.10.0+typescript@4.5.5
common-tags: 1.8.2
eslint: 8.10.0
eslint-etc: 5.1.0_eslint@8.10.0+typescript@4.5.5
requireindex: 1.2.0
tslib: 2.3.1
typescript: 4.5.5
transitivePeerDependencies:
- supports-color
dev: true
/eslint-plugin-rxjs/5.0.2_eslint@8.10.0+typescript@4.5.5:
resolution: {integrity: sha512-Q2wsEHWInhZ3uz5df+YbD4g/NPQqAeYHjJuEsxqgVS+XAsYCuVE2pj9kADdMFy4GsQy2jt7KP+TOrnq1i6bI5Q==}
peerDependencies:
eslint: ^8.0.0
typescript: ^4.0.0
dependencies:
'@typescript-eslint/experimental-utils': 5.11.0_eslint@8.10.0+typescript@4.5.5
common-tags: 1.8.2
decamelize: 5.0.1
eslint: 8.10.0
eslint-etc: 5.1.0_eslint@8.10.0+typescript@4.5.5
requireindex: 1.2.0
rxjs-report-usage: 1.0.6
tslib: 2.3.1
tsutils: 3.21.0_typescript@4.5.5
tsutils-etc: 1.4.1_tsutils@3.21.0+typescript@4.5.5
typescript: 4.5.5
transitivePeerDependencies:
- supports-color
dev: true
/eslint-scope/5.1.1: /eslint-scope/5.1.1:
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
@ -5700,6 +5802,11 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/kleur/3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
dev: true
/klona/2.0.5: /klona/2.0.5:
resolution: {integrity: sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==} resolution: {integrity: sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -7045,6 +7152,12 @@ packages:
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
dev: true dev: true
/prettier/2.5.1:
resolution: {integrity: sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
/pretty-bytes/5.6.0: /pretty-bytes/5.6.0:
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -7066,6 +7179,14 @@ packages:
retry: 0.12.0 retry: 0.12.0
dev: true dev: true
/prompts/2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
dependencies:
kleur: 3.0.3
sisteransi: 1.0.5
dev: true
/protractor/7.0.0: /protractor/7.0.0:
resolution: {integrity: sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==} resolution: {integrity: sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==}
engines: {node: '>=10.13.x'} engines: {node: '>=10.13.x'}
@ -7303,6 +7424,11 @@ packages:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
dev: true dev: true
/requireindex/1.2.0:
resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==}
engines: {node: '>=0.10.5'}
dev: true
/requires-port/1.0.0: /requires-port/1.0.0:
resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=} resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=}
dev: true dev: true
@ -7397,6 +7523,21 @@ packages:
rxjs: 6.6.7 rxjs: 6.6.7
dev: true dev: true
/rxjs-report-usage/1.0.6:
resolution: {integrity: sha512-omv1DIv5z1kV+zDAEjaDjWSkx8w5TbFp5NZoPwUipwzYVcor/4So9ZU3bUyQ1c8lxY5Q0Es/ztWW7PGjY7to0Q==}
hasBin: true
dependencies:
'@babel/parser': 7.17.3
'@babel/traverse': 7.17.3
'@babel/types': 7.17.0
bent: 7.3.12
chalk: 4.1.2
glob: 7.2.0
prompts: 2.4.2
transitivePeerDependencies:
- supports-color
dev: true
/rxjs/6.6.7: /rxjs/6.6.7:
resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==}
engines: {npm: '>=2.0.0'} engines: {npm: '>=2.0.0'}
@ -7631,6 +7772,10 @@ packages:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: true dev: true
/sisteransi/1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
dev: true
/slash/3.0.0: /slash/3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -8123,6 +8268,19 @@ packages:
/tslib/2.3.1: /tslib/2.3.1:
resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==}
/tsutils-etc/1.4.1_tsutils@3.21.0+typescript@4.5.5:
resolution: {integrity: sha512-6UPYgc7OXcIW5tFxlsZF3OVSBvDInl/BkS3Xsu64YITXk7WrnWTVByKWPCThFDBp5gl5IGHOzGMdQuDCE7OL4g==}
hasBin: true
peerDependencies:
tsutils: ^3.0.0
typescript: ^4.0.0
dependencies:
'@types/yargs': 17.0.9
tsutils: 3.21.0_typescript@4.5.5
typescript: 4.5.5
yargs: 17.3.1
dev: true
/tsutils/3.21.0_typescript@4.5.5: /tsutils/3.21.0_typescript@4.5.5:
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'} engines: {node: '>= 6'}

View File

@ -1,14 +1,17 @@
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core"
import { RouterModule, Routes } from '@angular/router'; import type { Routes } from "@angular/router"
import { environment } from '../environments/environment'; import { RouterModule } from "@angular/router"
import { environment } from "../environments/environment"
const routes: Routes = [{ const routes: Routes = [
path: '', {
loadChildren: () => import('./pages/pages.module').then(m => m.PagesModule), path: "",
}]; loadChildren: async () => import("./pages/pages.module").then(m => m.PagesModule),
},
]
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes, { useHash: environment.useHash, relativeLinkResolution: 'legacy' })], imports: [RouterModule.forRoot(routes, { useHash: environment.useHash, relativeLinkResolution: "legacy" })],
exports: [RouterModule] exports: [RouterModule],
}) })
export class AppRoutingModule { } export class AppRoutingModule {}

View File

@ -3,8 +3,7 @@
<!-- Theme Switcher --> <!-- Theme Switcher -->
<button mat-icon-button=""> <button mat-icon-button="">
<mat-icon (click)="themeService.switchTheme()">{{themeService.icon}}</mat-icon> <mat-icon (click)="themeService.switchTheme()">{{ themeService.icon }}</mat-icon>
</button> </button>
</mat-toolbar> </mat-toolbar>
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@ -1,35 +1,43 @@
import { TestBed, waitForAsync } from '@angular/core/testing'; import { TestBed, waitForAsync } from "@angular/core/testing"
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from "@angular/router/testing"
import { AppComponent } from './app.component'; import { AppComponent } from "./app.component"
describe('AppComponent', (): void => { describe("AppComponent", (): void => {
beforeEach(waitForAsync((): void => { beforeEach(
TestBed.configureTestingModule({ waitForAsync((): void => {
imports: [ void TestBed.configureTestingModule({
RouterTestingModule imports: [RouterTestingModule],
], declarations: [AppComponent],
declarations: [ }).compileComponents()
AppComponent })
], )
}).compileComponents();
}));
it('should create the app', (): void => { it("should create the app", (): void => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent)
const app = fixture.debugElement.componentInstance; if (!(fixture.debugElement.componentInstance instanceof AppComponent)) throw new Error("Expected AppComponent")
expect(app).toBeTruthy();
});
it(`should have as title 'public-gateway-cacher'`, (): void => { const app: AppComponent = fixture.debugElement.componentInstance
const fixture = TestBed.createComponent(AppComponent); void expect(app).toBeTruthy()
const app = fixture.debugElement.componentInstance; })
expect(app.title).toEqual('public-gateway-cacher');
});
it('should render title', (): void => { it(`should have a themeService`, (): void => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent)
fixture.detectChanges(); if (!(fixture.debugElement.componentInstance instanceof AppComponent)) throw new Error("Expected AppComponent")
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('public-gateway-cacher app is running!'); const app: AppComponent = fixture.debugElement.componentInstance
}); void expect(app.themeService).toBeTruthy()
}); })
it("should render title", (): void => {
const fixture = TestBed.createComponent(AppComponent)
fixture.detectChanges()
if (!isHTMLElement(fixture.debugElement.nativeElement)) throw new Error("Expected HTMLElement")
const compiled: HTMLElement = fixture.debugElement.nativeElement
void expect(compiled.querySelector("h1")?.textContent).toContain("Public Gateway Cacher")
})
})
function isHTMLElement(element: unknown): element is HTMLElement {
return Object.getPrototypeOf(element) instanceof HTMLElement
}

View File

@ -1,14 +1,12 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject } from "@angular/core"
import { ThemeService } from './services/theme.service'; import { ThemeService } from "./services/theme.service"
@Component({ @Component({
selector: 'app-root', selector: "app-root",
templateUrl: './app.component.html', templateUrl: "./app.component.html",
styleUrls: ['./app.component.scss'], styleUrls: ["./app.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AppComponent { export class AppComponent {
constructor( constructor(@Inject(ThemeService) readonly themeService: ThemeService) {}
public readonly themeService: ThemeService
) { }
} }

View File

@ -1,19 +1,17 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from "@angular/common"
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from "@angular/common/http"
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core"
import { FlexLayoutModule } from '@angular/flex-layout'; import { FlexLayoutModule } from "@angular/flex-layout"
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from "@angular/material/button"
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from "@angular/material/icon"
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from "@angular/material/toolbar"
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from "@angular/platform-browser"
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from "./app-routing.module"
import { AppComponent } from './app.component'; import { AppComponent } from "./app.component"
@NgModule({ @NgModule({
declarations: [ declarations: [AppComponent],
AppComponent
],
imports: [ imports: [
AppRoutingModule, AppRoutingModule,
BrowserAnimationsModule, BrowserAnimationsModule,
@ -26,6 +24,6 @@ import { AppComponent } from './app.component';
MatToolbarModule, MatToolbarModule,
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent],
}) })
export class AppModule { } export class AppModule {}

View File

@ -1,4 +1,4 @@
export enum Protocol { export enum Protocol {
IPFS = 'ipfs', IPFS = "ipfs",
IPNS = 'ipns', IPNS = "ipns",
} }

View File

@ -1,4 +1,4 @@
export enum Theme { export enum Theme {
Light = 'theme-light', Light = "theme-light",
Dark = 'theme-dark', Dark = "theme-dark",
} }

View File

@ -1,5 +1,5 @@
export interface Environment { export interface Environment {
production: boolean; production: boolean
base_href?: string; base_href?: string
useHash: boolean; useHash: boolean
} }

View File

@ -1,14 +1,17 @@
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core"
import { RouterModule, Routes } from '@angular/router'; import type { Routes } from "@angular/router"
import { PagesComponent } from './pages.component'; import { RouterModule } from "@angular/router"
import { PagesComponent } from "./pages.component"
const routes: Routes = [{ const routes: Routes = [
path: '', {
component: PagesComponent, path: "",
}]; component: PagesComponent,
},
]
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule] exports: [RouterModule],
}) })
export class PagesRoutingModule { } export class PagesRoutingModule {}

View File

@ -1,42 +1,40 @@
<div class="container" fxLayout="column" fxLayoutAlign="space-evenly center" fxLayoutGap="1em"> <div class="container" fxLayout="column" fxLayoutAlign="space-evenly center" fxLayoutGap="1em">
<!-- IPFS --> <!-- IPFS -->
<form fxLayout="row" fxLayoutAlign="space-evenly center" fxLayoutGap="1em"> <form fxLayout="row" fxLayoutAlign="space-evenly center" fxLayoutGap="1em">
<!-- Hash --> <!-- Hash -->
<mat-form-field [color]="inputColour"> <mat-form-field [color]="inputColour">
<mat-label>IPFS</mat-label> <mat-label>IPFS</mat-label>
<input name="ipfs" [(ngModel)]="ipfs" matInput> <input name="ipfs" [(ngModel)]="ipfs" matInput />
</mat-form-field> </mat-form-field>
<!-- Cache --> <!-- Cache -->
<button (click)="cacheIPFS()" type="button" mat-flat-button color="primary">Cache</button> <button (click)="cacheIPFS()" type="button" mat-flat-button color="primary">Cache</button>
</form> </form>
<!-- IPNS --> <!-- IPNS -->
<form fxLayout="row" fxLayoutAlign="space-evenly center" fxLayoutGap="1em"> <form fxLayout="row" fxLayoutAlign="space-evenly center" fxLayoutGap="1em">
<!-- Hash --> <!-- Hash -->
<mat-form-field [color]="inputColour"> <mat-form-field [color]="inputColour">
<mat-label>IPNS</mat-label> <mat-label>IPNS</mat-label>
<input name="ipns" [(ngModel)]="ipns" matInput> <input name="ipns" [(ngModel)]="ipns" matInput />
</mat-form-field> </mat-form-field>
<!-- Cache --> <!-- Cache -->
<button (click)="cacheIPNS()" type="button" mat-flat-button color="primary">Cache</button> <button (click)="cacheIPNS()" type="button" mat-flat-button color="primary">Cache</button>
</form> </form>
<mat-progress-bar [value]="dataSource.data.length / gateways.length * 100" *ngIf="subscriptions.length" mode="determinate" <mat-progress-bar
color="primary"> [value]="(dataSource.data.length / gateways.length) * 100"
*ngIf="subscriptions.length"
mode="determinate"
color="primary"
>
</mat-progress-bar> </mat-progress-bar>
<table class="mat-elevation-z8" [dataSource]="dataSource" mat-table aria-label="List of IPFS gateways and their statuses."> <table class="mat-elevation-z8" [dataSource]="dataSource" mat-table aria-label="List of IPFS gateways and their statuses.">
<!-- Result Column --> <!-- Result Column -->
<ng-container matColumnDef="icon"> <ng-container matColumnDef="icon">
<th mat-header-cell *matHeaderCellDef scope="col"> Status </th> <th mat-header-cell *matHeaderCellDef scope="col">Status</th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<span [matTooltip]="element.message"> {{ element.icon }} </span> <span [matTooltip]="element.message"> {{ element.icon }} </span>
</td> </td>
@ -44,7 +42,7 @@
<!-- Gateway Column --> <!-- Gateway Column -->
<ng-container matColumnDef="gateway"> <ng-container matColumnDef="gateway">
<th mat-header-cell *matHeaderCellDef scope="col"> Gateway </th> <th mat-header-cell *matHeaderCellDef scope="col">Gateway</th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<div [ngSwitch]="element.ok"> <div [ngSwitch]="element.ok">
<div *ngSwitchCase="true"> <div *ngSwitchCase="true">
@ -64,24 +62,21 @@
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table> </table>
<!-- About --> <!-- About -->
<div fxLayout="column" fxLayoutAlign="space-evenly start" fxLayoutGap="1em"> <div fxLayout="column" fxLayoutAlign="space-evenly start" fxLayoutGap="1em">
<h3>About</h3> <h3>About</h3>
<p> <p>
This allows you to cache a specific IPFS hash to a bunch of public gateways. This allows you to cache a specific IPFS hash to a bunch of public gateways. It's inspired from
It's inspired from <a class="link" href="https://github.com/ipfs/public-gateway-checker">github.com/ipfs/public-gateway-checker</a>. <a class="link" href="https://github.com/ipfs/public-gateway-checker">github.com/ipfs/public-gateway-checker</a>. The source code is
The source code is available at <a class="link" available at <a class="link" href="https://gitlab.com/NatoBoram/public-gateway-cacher">gitlab.com/NatoBoram/public-gateway-cacher</a>.
href="https://gitlab.com/NatoBoram/public-gateway-cacher">gitlab.com/NatoBoram/public-gateway-cacher</a>.
</p> </p>
<p> <p>
If you'd like to add a new public gateway, If you'd like to add a new public gateway, please go to
please go to <a class="link" href="https://github.com/ipfs/public-gateway-checker">github.com/ipfs/public-gateway-checker</a>, <a class="link" href="https://github.com/ipfs/public-gateway-checker">github.com/ipfs/public-gateway-checker</a>, submit a pull
submit a pull request then open an issue <a class="link" request then open an issue <a class="link" href="https://gitlab.com/NatoBoram/public-gateway-cacher/issues/new">here</a>.
href="https://gitlab.com/NatoBoram/public-gateway-cacher/issues/new">here</a>.
</p> </p>
</div> </div>
</div> </div>

View File

@ -1,24 +1,28 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { HttpClientTestingModule } from "@angular/common/http/testing"
import { PagesComponent } from './pages.component'; import type { ComponentFixture } from "@angular/core/testing"
import { TestBed, waitForAsync } from "@angular/core/testing"
import { PagesComponent } from "./pages.component"
describe('PagesComponent', (): void => { describe("PagesComponent", (): void => {
let component: PagesComponent; let component: PagesComponent
let fixture: ComponentFixture<PagesComponent>; let fixture: ComponentFixture<PagesComponent>
beforeEach(waitForAsync((): void => { beforeEach(
TestBed.configureTestingModule({ waitForAsync((): void => {
declarations: [PagesComponent] void TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
declarations: [PagesComponent],
}).compileComponents()
}) })
.compileComponents(); )
}));
beforeEach((): void => { beforeEach((): void => {
fixture = TestBed.createComponent(PagesComponent); fixture = TestBed.createComponent(PagesComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', (): void => { it("should create", (): void => {
expect(component).toBeTruthy(); void expect(component).toBeTruthy()
}); })
}); })

View File

@ -1,134 +1,151 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from "@angular/common/http"
import { ChangeDetectionStrategy, Component, EventEmitter, OnDestroy, OnInit, ViewChild } from '@angular/core'; import type { OnDestroy, OnInit } from "@angular/core"
import { ThemePalette } from '@angular/material/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Inject, ViewChild } from "@angular/core"
import { MatTable, MatTableDataSource } from '@angular/material/table'; import type { ThemePalette } from "@angular/material/core"
import { Subscription } from 'rxjs'; import { MatTable, MatTableDataSource } from "@angular/material/table"
import { takeUntil } from 'rxjs/operators'; import type { Subscription } from "rxjs"
import { environment } from '../../environments/environment'; import { takeUntil } from "rxjs/operators"
import { Protocol } from '../enums/protocol.enum'; import { environment } from "../../environments/environment"
import { Theme } from '../enums/theme.enum'; import { Protocol } from "../enums/protocol.enum"
import { GatewayService } from '../services/gateway.service'; import { Theme } from "../enums/theme.enum"
import { ThemeService } from '../services/theme.service'; import { GatewayService } from "../services/gateway.service"
import { ThemeService } from "../services/theme.service"
@Component({ @Component({
selector: 'app-pages', selector: "app-pages",
templateUrl: './pages.component.html', templateUrl: "./pages.component.html",
styleUrls: ['./pages.component.scss'], styleUrls: ["./pages.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class PagesComponent implements OnInit, OnDestroy { export class PagesComponent implements OnInit, OnDestroy {
@ViewChild(MatTable) matTable!: MatTable<Result>
@ViewChild(MatTable) matTable!: MatTable<Result>; gateways!: string[]
inputColour: ThemePalette = "primary"
ipfs = ""
ipns = ""
gateways!: string[]; readonly dataSource = new MatTableDataSource<Result>([])
inputColour: ThemePalette = 'primary'; readonly displayedColumns = ["icon", "gateway"]
ipfs = ''; readonly subscriptions: Subscription[] = []
ipns = '';
readonly dataSource = new MatTableDataSource<Result>([]); private readonly destroy$ = new EventEmitter<void>()
readonly displayedColumns: ['icon', 'gateway'] = ['icon', 'gateway'];
readonly subscriptions: Subscription[] = [];
private readonly destroy$ = new EventEmitter<void>();
constructor( constructor(
private readonly gatewayService: GatewayService, @Inject(GatewayService) private readonly gatewayService: GatewayService,
private readonly themeService: ThemeService @Inject(ThemeService) private readonly themeService: ThemeService
) { } ) {}
ngOnInit(): void { ngOnInit(): void {
this.gatewayService.list().subscribe((gateways): void => { this.gateways = gateways; }); this.gatewayService
.list()
.pipe(takeUntil(this.destroy$))
.subscribe((gateways): void => {
this.gateways = gateways
})
// Theme // Theme
this.setColours(this.themeService.current); this.setColours(this.themeService.current)
this.themeService.valueChanges.pipe( this.themeService.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((theme): void => {
takeUntil(this.destroy$) this.setColours(theme)
).subscribe((theme): void => { })
this.setColours(theme);
});
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.destroy$.complete(); this.destroy$.next()
this.destroy$.complete()
} }
cacheIPFS(): void { cacheIPFS(): void {
this.ipfs = this.ipfs.trim(); this.ipfs = this.ipfs.trim()
this.cache(Protocol.IPFS, this.ipfs); this.cache(Protocol.IPFS, this.ipfs)
} }
cacheIPNS(): void { cacheIPNS(): void {
this.ipns = this.ipns.trim(); this.ipns = this.ipns.trim()
this.cache(Protocol.IPNS, this.ipns); this.cache(Protocol.IPNS, this.ipns)
} }
cache(protocol: Protocol, hashpath: string): void { cache(protocol: Protocol, hashpath: string): void {
// Clear subscriptions // Clear subscriptions
while (this.subscriptions.length) { while (this.subscriptions.length) {
const sub = this.subscriptions.pop(); const sub = this.subscriptions.pop()
if (sub && !sub.closed) { if (sub && !sub.closed) {
sub.unsubscribe(); sub.unsubscribe()
} }
} }
// Clear table // Clear table
this.dataSource.data = []; this.dataSource.data = []
this.matTable.renderRows(); this.matTable.renderRows()
console.clear(); console.clear()
this.gateways.forEach((gateway): void => { this.gateways.forEach((gateway): void => {
this.subscriptions.push( this.subscriptions.push(
this.gatewayService.get(gateway, protocol, hashpath).subscribe((resp): void => { this.gatewayService
this.dataSource.data.push({ .get(gateway, protocol, hashpath)
gateway: this.gatewayService.url(gateway, protocol, hashpath), .pipe(takeUntil(this.destroy$))
message: resp.statusText, .subscribe(
icon: this.getIcon(resp.status), (resp): void => {
ok: resp.ok, this.dataSource.data.push({
}); gateway: this.gatewayService.url(gateway, protocol, hashpath),
this.matTable.renderRows(); message: resp.statusText,
}, (error: HttpErrorResponse): void => { icon: this.getIcon(resp.status),
this.dataSource.data.push({ ok: resp.ok,
gateway: this.gatewayService.url(gateway, protocol, hashpath), })
message: error.statusText, this.matTable.renderRows()
icon: this.getIcon(error.status), },
ok: error.ok, (error: unknown): void => {
}); if (!(error instanceof HttpErrorResponse)) return
this.matTable.renderRows();
}) this.dataSource.data.push({
); gateway: this.gatewayService.url(gateway, protocol, hashpath),
}); message: error.statusText,
icon: this.getIcon(error.status),
ok: error.ok,
})
this.matTable.renderRows()
}
)
)
})
} }
private setColours(theme: Theme): void { private setColours(theme: Theme): void {
switch (theme) { switch (theme) {
case Theme.Light: case Theme.Light:
this.inputColour = 'primary'; this.inputColour = "primary"
break; break
case Theme.Dark: case Theme.Dark:
this.inputColour = 'accent'; this.inputColour = "accent"
break; break
default: default:
break; break
} }
} }
private getIcon(status: number): string { private getIcon(status: number): string {
if (status >= 200 && status < 300) { return '✅'; } if (status >= 200 && status < 300) {
return "✅"
}
switch (status) { switch (status) {
case 0: return '❌'; case 0:
case 403: return '⛔'; return "❌"
case 404: return '❓'; case 403:
case 500: return '❗'; return "⛔"
default: return environment.production ? '❌' : '❔'; case 404:
return "❓"
case 500:
return "❗"
default:
return environment.production ? "❌" : "❔"
} }
} }
} }
interface Result { interface Result {
gateway: string; gateway: string
message: string; message: string
icon: string; icon: string
ok: boolean; ok: boolean
} }

View File

@ -1,15 +1,15 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from "@angular/common"
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core"
import { FlexLayoutModule } from '@angular/flex-layout'; import { FlexLayoutModule } from "@angular/flex-layout"
import { FormsModule } from '@angular/forms'; import { FormsModule } from "@angular/forms"
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from "@angular/material/button"
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from "@angular/material/input"
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressBarModule } from "@angular/material/progress-bar"
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from "@angular/material/sort"
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from "@angular/material/table"
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from "@angular/material/tooltip"
import { PagesRoutingModule } from './pages-routing.module'; import { PagesRoutingModule } from "./pages-routing.module"
import { PagesComponent } from './pages.component'; import { PagesComponent } from "./pages.component"
@NgModule({ @NgModule({
declarations: [PagesComponent], declarations: [PagesComponent],
@ -24,6 +24,6 @@ import { PagesComponent } from './pages.component';
MatTableModule, MatTableModule,
MatTooltipModule, MatTooltipModule,
PagesRoutingModule, PagesRoutingModule,
] ],
}) })
export class PagesModule { } export class PagesModule {}

View File

@ -1,11 +1,18 @@
import { TestBed, TestBedStatic } from '@angular/core/testing'; import { HttpClientTestingModule } from "@angular/common/http/testing"
import { GatewayService } from './gateway.service'; import type { TestBedStatic } from "@angular/core/testing"
import { TestBed } from "@angular/core/testing"
import { GatewayService } from "./gateway.service"
describe('GatewayService', (): void => { describe("GatewayService", (): void => {
beforeEach((): TestBedStatic => TestBed.configureTestingModule({})); beforeEach(
(): TestBedStatic =>
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
})
)
it('should be created', (): void => { it("should be created", (): void => {
const service: GatewayService = TestBed.inject(GatewayService); const service: GatewayService = TestBed.inject(GatewayService)
expect(service).toBeTruthy(); void expect(service).toBeTruthy()
}); })
}); })

View File

@ -1,37 +1,34 @@
import { HttpClient, HttpResponse } from '@angular/common/http'; import type { HttpResponse } from "@angular/common/http"
import { Injectable } from '@angular/core'; import { HttpClient } from "@angular/common/http"
import { Observable } from 'rxjs'; import { Inject, Injectable } from "@angular/core"
import { environment } from '../../environments/environment'; import type { Observable } from "rxjs"
import { Protocol } from '../enums/protocol.enum'; import { environment } from "../../environments/environment"
import type { Protocol } from "../enums/protocol.enum"
@Injectable({ @Injectable({
providedIn: 'root' providedIn: "root",
}) })
export class GatewayService { export class GatewayService {
constructor(@Inject(HttpClient) private readonly http: HttpClient) {}
constructor(
private readonly http: HttpClient
) { }
list(): Observable<string[]> { list(): Observable<string[]> {
return this.http.get<string[]>( return this.http.get<string[]>(
environment.base_href && environment.base_href !== '/' environment.base_href && environment.base_href !== "/"
? `${environment.base_href}/assets/json/gateways.json` ? `${environment.base_href}/assets/json/gateways.json`
: `${document.querySelector('base')?.href}assets/json/gateways.json` : `${document.querySelector("base")?.href ?? ""}assets/json/gateways.json`
); )
} }
get(gateway: string, protocol: Protocol, hashpath: string): Observable<HttpResponse<string>> { get(gateway: string, protocol: Protocol, hashpath: string): Observable<HttpResponse<string>> {
return this.http.get(`${this.url(gateway, protocol, hashpath)}#x-ipfs-companion-no-redirect`, { return this.http.get(`${this.url(gateway, protocol, hashpath)}#x-ipfs-companion-no-redirect`, {
observe: 'response', observe: "response",
responseType: 'text', responseType: "text",
}); })
} }
url(gateway: string, protocol: Protocol, hashpath: string): string { url(gateway: string, protocol: Protocol, hashpath: string): string {
const splits: string[] = hashpath.split('/'); const splits: string[] = hashpath.split("/")
const url: string = gateway.replace(':type', protocol).replace(':hash', splits.shift() || ''); const url: string = gateway.replace(":type", protocol).replace(":hash", splits.shift() ?? "")
return splits.length ? [url, splits.join('/')].join('/') : url; return splits.length ? [url, splits.join("/")].join("/") : url
} }
} }

View File

@ -1,15 +1,15 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from "@angular/core/testing"
import { ThemeService } from './theme.service'; import { ThemeService } from "./theme.service"
describe('ThemeService', (): void => { describe("ThemeService", (): void => {
let service: ThemeService; let service: ThemeService
beforeEach((): void => { beforeEach((): void => {
TestBed.configureTestingModule({}); TestBed.configureTestingModule({})
service = TestBed.inject(ThemeService); service = TestBed.inject(ThemeService)
}); })
it('should be created', (): void => { it("should be created", (): void => {
expect(service).toBeTruthy(); void expect(service).toBeTruthy()
}); })
}); })

View File

@ -1,49 +1,51 @@
import { Injectable } from '@angular/core'; import { Injectable } from "@angular/core"
import { Observable, Subject } from 'rxjs'; import type { Observable } from "rxjs"
import { Theme } from '../enums/theme.enum'; import { Subject } from "rxjs"
import { Theme } from "../enums/theme.enum"
function enumGuard<T>(enumeration: T): (token: unknown) => token is T[keyof T] { function enumGuard<T>(enumeration: T): (token: unknown) => token is T[keyof T] {
return (token: unknown): token is T[keyof T] => Object.values(enumeration).includes(token); return (token: unknown): token is T[keyof T] => Object.values(enumeration).includes(token)
} }
@Injectable({ @Injectable({
providedIn: 'root' providedIn: "root",
}) })
export class ThemeService { export class ThemeService {
current = Theme.Light
icon = "brightness_low"
readonly valueChanges: Observable<Theme>
current = Theme.Light; private readonly subject$ = new Subject<Theme>()
icon = 'brightness_low';
readonly valueChanges: Observable<Theme>;
private readonly subject$ = new Subject<Theme>();
constructor() { constructor() {
const stored = localStorage.getItem('theme'); const stored = localStorage.getItem("theme")
if (enumGuard(Theme)(stored)) { if (enumGuard(Theme)(stored)) {
this.setTheme(stored); this.setTheme(stored)
} }
this.valueChanges = this.subject$.asObservable(); this.valueChanges = this.subject$.asObservable()
} }
switchTheme(): void { switchTheme(): void {
this.setTheme(this.current === Theme.Light ? Theme.Dark : Theme.Light); this.setTheme(this.current === Theme.Light ? Theme.Dark : Theme.Light)
} }
setTheme(theme: Theme): void { setTheme(theme: Theme): void {
document.querySelector('body')?.classList.remove(this.current); document.querySelector("body")?.classList.remove(this.current)
document.querySelector('body')?.classList.add(theme); document.querySelector("body")?.classList.add(theme)
this.current = theme; this.current = theme
this.icon = this.getIcon(theme); this.icon = this.getIcon(theme)
this.subject$.next(theme); this.subject$.next(theme)
localStorage.setItem('theme', theme); localStorage.setItem("theme", theme)
} }
getIcon(theme: Theme): string { getIcon(theme: Theme): string {
switch (theme) { switch (theme) {
case Theme.Dark: return 'brightness_high'; case Theme.Dark:
return "brightness_high"
case Theme.Light: case Theme.Light:
default: return 'brightness_low'; default:
return "brightness_low"
} }
} }
} }

View File

@ -1,7 +1,7 @@
import { Environment } from '../app/interfaces/environment'; import type { Environment } from "../app/interfaces/environment"
export const environment: Environment = { export const environment: Environment = {
production: true, production: true,
base_href: '/public-gateway-cacher', base_href: "/public-gateway-cacher",
useHash: false, useHash: false,
}; }

View File

@ -1,6 +1,6 @@
import { Environment } from '../app/interfaces/environment'; import type { Environment } from "../app/interfaces/environment"
export const environment: Environment = { export const environment: Environment = {
production: true, production: true,
useHash: true, useHash: true,
}; }

View File

@ -1,7 +1,7 @@
import { Environment } from '../app/interfaces/environment'; import type { Environment } from "../app/interfaces/environment"
export const environment: Environment = { export const environment: Environment = {
production: true, production: true,
base_href: '/', base_href: "/",
useHash: false, useHash: false,
}; }

View File

@ -1,4 +1,4 @@
import { Environment } from '../app/interfaces/environment'; import type { Environment } from "../app/interfaces/environment"
// This file can be replaced during build by using the `fileReplacements` array. // This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
@ -6,9 +6,9 @@ import { Environment } from '../app/interfaces/environment';
export const environment: Environment = { export const environment: Environment = {
production: false, production: false,
base_href: '/', base_href: "/",
useHash: false, useHash: false,
}; }
/* /*
* For easier debugging in development mode, you can import the following file * For easier debugging in development mode, you can import the following file

View File

@ -1,26 +1,23 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<title>Public Gateway Cacher</title>
<head> <!-- IPFS -->
<meta charset="utf-8"> <script>
<title>Public Gateway Cacher</title> if (document.getElementsByTagName("base").length === 0) document.write('<base href="' + window.location.pathname + '"/>')
</script>
<!-- IPFS --> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script> <link rel="icon" type="image/x-icon" href="favicon.png" />
if (document.getElementsByTagName('base').length === 0) document.write('<base href="' + window.location.pathname + '"/>');
</script>
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Material -->
<link rel="icon" type="image/x-icon" href="favicon.png"> <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<!-- Material --> </head>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-app-background theme-light">
<app-root></app-root>
</body>
<body class="mat-app-background theme-light">
<app-root></app-root>
</body>
</html> </html>

View File

@ -1,11 +1,12 @@
import { enableProdMode } from '@angular/core'; import { enableProdMode } from "@angular/core"
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"
import { AppModule } from './app/app.module'; import { AppModule } from "./app/app.module"
import { environment } from './environments/environment'; import { environment } from "./environments/environment"
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode()
} }
platformBrowserDynamic().bootstrapModule(AppModule) platformBrowserDynamic()
.catch((err): void => console.error(err)); .bootstrapModule(AppModule)
.catch((err): void => console.error(err))

View File

@ -45,8 +45,7 @@
/*************************************************************************************************** /***************************************************************************************************
* Zone JS is required by default for Angular itself. * Zone JS is required by default for Angular itself.
*/ */
import 'zone.js'; // Included with Angular CLI. import "zone.js" // Included with Angular CLI.
/*************************************************************************************************** /***************************************************************************************************
* APPLICATION IMPORTS * APPLICATION IMPORTS

View File

@ -1,5 +1,5 @@
// Custom Theming for Angular Material // Custom Theming for Angular Material
@use '@angular/material'as mat; @use "@angular/material" as mat;
// For more information: https://material.angular.io/guide/theming // For more information: https://material.angular.io/guide/theming
// Plus imports for other components in your app. // Plus imports for other components in your app.
@ -25,7 +25,7 @@
// // that you are using. // // that you are using.
// @include angular-material-theme($public-gateway-cacher-theme); // @include angular-material-theme($public-gateway-cacher-theme);
@import 'styles/ipfs-themes.scss'; @import "styles/ipfs-themes.scss";
.theme-light { .theme-light {
@include mat.all-component-themes($ipfs-light-theme); @include mat.all-component-themes($ipfs-light-theme);
@ -47,5 +47,5 @@ body {
font-family: Roboto, "Helvetica Neue", sans-serif; font-family: Roboto, "Helvetica Neue", sans-serif;
} }
@import '~bootstrap/scss/bootstrap-grid.scss'; @import "~bootstrap/scss/bootstrap-grid.scss";
@import '~ipfs-css/theme.scss'; @import "~ipfs-css/theme.scss";

View File

@ -1,25 +1,34 @@
@import "@angular/material/core/theming/palette"; @import "@angular/material/core/theming/palette";
$ipfs-colour-navy: (default: #0B3A53, $ipfs-colour-navy: (
default: #0b3a53,
lighter: #3e6480, lighter: #3e6480,
darker: #00142a, darker: #00142a,
contrast: (default: $light-primary-text, contrast: (
default: $light-primary-text,
lighter: $light-primary-text, lighter: $light-primary-text,
darker: $light-primary-text, darker: $light-primary-text,
)); ),
);
$ipfs-colour-aqua: (default: #69c5cd, $ipfs-colour-aqua: (
default: #69c5cd,
lighter: #9df8ff, lighter: #9df8ff,
darker: #32949c, darker: #32949c,
contrast: (default: $dark-primary-text, contrast: (
default: $dark-primary-text,
lighter: $dark-primary-text, lighter: $dark-primary-text,
darker: $dark-primary-text, darker: $dark-primary-text,
)); ),
);
$ipfs-colour-yellow: (default: #f39021, $ipfs-colour-yellow: (
default: #f39021,
lighter: #ffc155, lighter: #ffc155,
darker: #bb6200, darker: #bb6200,
contrast: (default: $dark-primary-text, contrast: (
default: $dark-primary-text,
lighter: $dark-primary-text, lighter: $dark-primary-text,
darker: $dark-primary-text, darker: $dark-primary-text,
)); ),
);

View File

@ -1,6 +1,6 @@
@use '@angular/material'as mat; @use "@angular/material" as mat;
@import 'ipfs-colours.scss'; @import "ipfs-colours.scss";
$ipfs-primary: mat.define-palette($ipfs-colour-navy, 'default', 'lighter', 'darker'); $ipfs-primary: mat.define-palette($ipfs-colour-navy, "default", "lighter", "darker");
$ipfs-accent: mat.define-palette($ipfs-colour-aqua, 'default', 'lighter', 'darker'); $ipfs-accent: mat.define-palette($ipfs-colour-aqua, "default", "lighter", "darker");
$ipfs-warn: mat.define-palette($ipfs-colour-yellow, 'default', 'lighter', 'darker'); $ipfs-warn: mat.define-palette($ipfs-colour-yellow, "default", "lighter", "darker");

View File

@ -1,5 +1,5 @@
@use '@angular/material'as mat; @use "@angular/material" as mat;
@import 'ipfs-palettes.scss'; @import "ipfs-palettes.scss";
$ipfs-light-theme: mat.define-light-theme($ipfs-primary, $ipfs-accent, $ipfs-warn); $ipfs-light-theme: mat.define-light-theme($ipfs-primary, $ipfs-accent, $ipfs-warn);
$ipfs-dark-theme: mat.define-dark-theme($ipfs-primary, $ipfs-accent, $ipfs-warn); $ipfs-dark-theme: mat.define-dark-theme($ipfs-primary, $ipfs-accent, $ipfs-warn);

View File

@ -1,22 +1,16 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files // This file is required by karma.conf.js and loads recursively all the .spec and framework files
import "zone.js/testing"
import { getTestBed } from '@angular/core/testing'; import { getTestBed } from "@angular/core/testing"
import { import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing"
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
import 'zone.js/testing';
declare const require: any; declare const require: any
// First, initialize the Angular testing environment. // First, initialize the Angular testing environment.
getTestBed().initTestEnvironment( getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: false } })
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
}
);
// Then we find all the tests. // Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/); const context = require.context("./", true, /\.spec\.ts$/)
// And load the modules. // And load the modules.
context.keys().map(context); context.keys().map(context)

View File

@ -5,11 +5,6 @@
"outDir": "./out-tsc/app", "outDir": "./out-tsc/app",
"types": [] "types": []
}, },
"files": [ "files": ["src/main.ts", "src/polyfills.ts"],
"src/main.ts", "include": ["src/**/*.d.ts"]
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
} }

View File

@ -30,10 +30,7 @@
"importHelpers": true, "importHelpers": true,
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"lib": [ "lib": ["ESNext", "DOM"]
"ESNext",
"DOM"
]
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"fullTemplateTypeCheck": true, "fullTemplateTypeCheck": true,

View File

@ -3,16 +3,8 @@
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/spec", "outDir": "./out-tsc/spec",
"types": [ "types": ["jasmine"]
"jasmine"
]
}, },
"files": [ "files": ["src/test.ts", "src/polyfills.ts"],
"src/test.ts", "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
} }