Turn domain logic into its own component

This commit is contained in:
Hunter 2019-09-05 12:34:45 -04:00
parent bef658a6ef
commit 5ed12f6fd6
No known key found for this signature in database
GPG key ID: 54A2E2B4055799B8
11 changed files with 443 additions and 198 deletions

8
.editorconfig Normal file
View file

@ -0,0 +1,8 @@
root = true
[*.(vue,js,ts)]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

209
package-lock.json generated
View file

@ -1073,6 +1073,12 @@
} }
} }
}, },
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
"dev": true
},
"@types/events": { "@types/events": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@ -1090,6 +1096,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/json-schema": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
"integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
"dev": true
},
"@types/minimatch": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -1114,6 +1126,83 @@
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
"dev": true "dev": true
}, },
"@types/webpack-env": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.14.0.tgz",
"integrity": "sha512-Fv+0gYJzE/czLoRKq+gnXWr4yBpPM3tO3C8pDLFwqVKlMICQUq5OsxwwFZYDaVr7+L6mgNDp16iOcJHEz3J5RQ==",
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz",
"integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "1.13.0",
"eslint-utils": "^1.3.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^2.0.1",
"tsutils": "^3.7.0"
},
"dependencies": {
"regexpp": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"dev": true
},
"tsutils": {
"version": "3.17.1",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
"integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
}
}
},
"@typescript-eslint/experimental-utils": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz",
"integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "1.13.0",
"eslint-scope": "^4.0.0"
}
},
"@typescript-eslint/parser": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz",
"integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "1.13.0",
"@typescript-eslint/typescript-estree": "1.13.0",
"eslint-visitor-keys": "^1.0.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz",
"integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==",
"dev": true,
"requires": {
"lodash.unescape": "4.0.1",
"semver": "5.5.0"
},
"dependencies": {
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true
}
}
},
"@vue/babel-helper-vue-jsx-merge-props": { "@vue/babel-helper-vue-jsx-merge-props": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz",
@ -1460,6 +1549,22 @@
} }
} }
}, },
"@vue/cli-plugin-typescript": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-typescript/-/cli-plugin-typescript-3.11.0.tgz",
"integrity": "sha512-oL0ctNVvbD7gZr3DDv6gxxWjw0lUrh4sGMk7InCakEooo/790DqZRX6lx9stXEv/+zELkgddcY3hjNEopbJg+w==",
"dev": true,
"requires": {
"@types/webpack-env": "^1.13.9",
"@vue/cli-shared-utils": "^3.11.0",
"fork-ts-checker-webpack-plugin": "^0.5.2",
"globby": "^9.2.0",
"ts-loader": "^5.3.3",
"tslint": "^5.15.0",
"webpack": "^4.0.0",
"yorkie": "^2.0.0"
}
},
"@vue/cli-service": { "@vue/cli-service": {
"version": "3.11.0", "version": "3.11.0",
"resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-3.11.0.tgz", "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-3.11.0.tgz",
@ -1601,6 +1706,16 @@
} }
} }
}, },
"@vue/eslint-config-typescript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-4.0.0.tgz",
"integrity": "sha512-uSMAMgw4xDgVdZQhpbtJRo8nMV4oOy3Ht8olfOo7xvYFYLMF2JZ1tDRKd9/NSusxA72O2Vma+HzmyzDHg9evcQ==",
"dev": true,
"requires": {
"@typescript-eslint/eslint-plugin": "^1.1.0",
"@typescript-eslint/parser": "^1.1.0"
}
},
"@vue/preload-webpack-plugin": { "@vue/preload-webpack-plugin": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.1.tgz",
@ -2674,6 +2789,12 @@
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
"dev": true "dev": true
}, },
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
"dev": true
},
"builtin-status-codes": { "builtin-status-codes": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
@ -3472,9 +3593,9 @@
} }
}, },
"core-js": { "core-js": {
"version": "3.2.1", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz",
"integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==" "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A=="
}, },
"core-js-compat": { "core-js-compat": {
"version": "3.2.1", "version": "3.2.1",
@ -4145,6 +4266,12 @@
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true "dev": true
}, },
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"diffie-hellman": { "diffie-hellman": {
"version": "5.0.3", "version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@ -5372,6 +5499,20 @@
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
}, },
"fork-ts-checker-webpack-plugin": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.5.2.tgz",
"integrity": "sha512-a5IG+xXyKnpruI0CP/anyRLAoxWtp3lzdG6flxicANnoSzz64b12dJ7ASAVRrI2OaWwZR2JyBaMHFQqInhWhIw==",
"dev": true,
"requires": {
"babel-code-frame": "^6.22.0",
"chalk": "^2.4.1",
"chokidar": "^2.0.4",
"micromatch": "^3.1.10",
"minimatch": "^3.0.4",
"tapable": "^1.0.0"
}
},
"form-data": { "form-data": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@ -7466,6 +7607,12 @@
"integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=", "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=",
"dev": true "dev": true
}, },
"lodash.unescape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
"integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
"dev": true
},
"lodash.uniq": { "lodash.uniq": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@ -11426,12 +11573,55 @@
"integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==",
"dev": true "dev": true
}, },
"ts-loader": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
"integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==",
"dev": true,
"requires": {
"chalk": "^2.3.0",
"enhanced-resolve": "^4.0.0",
"loader-utils": "^1.0.2",
"micromatch": "^3.1.4",
"semver": "^5.0.1"
}
},
"tslib": { "tslib": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true "dev": true
}, },
"tslint": {
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.19.0.tgz",
"integrity": "sha512-1LwwtBxfRJZnUvoS9c0uj8XQtAnyhWr9KlNvDIdB+oXyT+VpsOAaEhEgKi1HrZ8rq0ki/AAnbGSv4KM6/AfVZw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"builtin-modules": "^1.1.1",
"chalk": "^2.3.0",
"commander": "^2.12.1",
"diff": "^3.2.0",
"glob": "^7.1.1",
"js-yaml": "^3.13.1",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"resolve": "^1.3.2",
"semver": "^5.3.0",
"tslib": "^1.8.0",
"tsutils": "^2.29.0"
}
},
"tsutils": {
"version": "2.29.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
@ -11817,6 +12007,11 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
"integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==" "integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
}, },
"vue-class-component": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.1.0.tgz",
"integrity": "sha512-G9152NzUkz0i0xTfhk0Afc8vzdXxDR1pfN4dTwE72cskkgJtdXfrKBkMfGvDuxUh35U500g5Ve4xL8PEGdWeHg=="
},
"vue-eslint-parser": { "vue-eslint-parser": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",
@ -11874,6 +12069,14 @@
"vue-style-loader": "^4.1.0" "vue-style-loader": "^4.1.0"
} }
}, },
"vue-property-decorator": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-8.2.2.tgz",
"integrity": "sha512-3gRrIeoUtjXvkoMX2stJsVs7805Pa9MXEndnk21ej+sWO7AIc5HF1TKqK0Pox5TEjpO02UbadIF0QWNrx6ZwXQ==",
"requires": {
"vue-class-component": "^7.0.1"
}
},
"vue-style-loader": { "vue-style-loader": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",

View file

@ -5,27 +5,31 @@
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "cross-env NODE_ENV=production vue-cli-service build", "build": "cross-env NODE_ENV=production vue-cli-service build",
"populate": "cross-env NODE_ENV=production tsc misc/populate_list.ts && cross-env node misc/populate_list.js", "lint": "vue-cli-service lint",
"lint": "vue-cli-service lint" "populate": "cross-env NODE_ENV=production tsc misc/populate_list.ts && cross-env node misc/populate_list.js"
}, },
"dependencies": { "dependencies": {
"axios": "^0.19.0", "axios": "^0.19.0",
"bulma": "^0.7.5", "bulma": "^0.7.5",
"core-js": "^3.2.1", "core-js": "^2.6.9",
"node-sass": "^4.12.0", "node-sass": "^4.12.0",
"sass-loader": "^8.0.0", "sass-loader": "^8.0.0",
"vue": "^2.6.10" "vue": "^2.6.10",
"vue-class-component": "^7.1.0",
"vue-property-decorator": "^8.2.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.5.5", "@babel/preset-env": "^7.5.5",
"@vue/cli-plugin-babel": "^3.11.0", "@vue/cli-plugin-babel": "^3.11.0",
"@vue/cli-plugin-eslint": "^3.11.0", "@vue/cli-plugin-eslint": "^3.11.0",
"@vue/cli-service": "^3.11.0", "@vue/cli-service": "^3.11.0",
"@vue/eslint-config-typescript": "^4.0.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"eslint": "^6.3.0", "eslint": "^6.3.0",
"eslint-plugin-vue": "^5.2.3", "eslint-plugin-vue": "^5.2.3",
"typescript": "^3.6.2", "typescript": "^3.6.2",
"@vue/cli-plugin-typescript": "^3.11.0",
"vue-template-compiler": "^2.6.10" "vue-template-compiler": "^2.6.10"
}, },
"eslintConfig": { "eslintConfig": {
@ -35,11 +39,12 @@
}, },
"extends": [ "extends": [
"plugin:vue/essential", "plugin:vue/essential",
"eslint:recommended" "eslint:recommended",
"@vue/typescript"
], ],
"rules": {}, "rules": {},
"parserOptions": { "parserOptions": {
"parser": "babel-eslint" "parser": "@typescript-eslint/parser"
} }
}, },
"postcss": { "postcss": {

View file

@ -6,7 +6,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="preload" href="<$= BASE_URL %>iata.json" as="fetch">
<title>cloudflare-test.judge.sh</title> <title>cloudflare-test.judge.sh</title>
</head> </head>

View file

@ -1,18 +1,19 @@
<template> <template>
<div id="app" class="container"> <div id="app" class="container">
<Main/> <Main/>
</div> </div>
</template> </template>
<script> <script lang="ts">
import Main from './components/Main.vue' import { Component, Vue } from 'vue-property-decorator';
import Main from './components/Main.vue';
export default { @Component({
name: 'app',
components: { components: {
Main Main,
} },
} })
export default class App extends Vue {}
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -0,0 +1,66 @@
<template>
<div>
<p class="heading" v-text="hostname" />
<div v-if="finished">
<p class="title" v-text="datacenter" />
<p class="heading" v-text="airport" />
<p class="heading">First ping: {{ firstPing }}</p>
<p class="heading">Second ping: {{ secondPing }}</p>
</div>
<div v-if="broken">
<p>Likely not a Cloudflare website, or not proxied</p>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
import Axios from "axios";
@Component
export default class DomainItem extends Vue {
// @ts-ignore
@Prop(String) readonly hostname: string;
finished = false;
broken = false;
datacenter = "";
airport = "";
firstPing = 0;
secondPing = 0;
mounted() {
this.loadHostname(this.hostname)
.then(datacenter => {
this.datacenter = datacenter;
// @ts-ignore
this.airport = this.$parent.getAirport(datacenter);
})
.then(() => this.loadHostname(this.hostname, 1))
.then(() => {
this.firstPing = this.loadPing(this.hostname, 0);
this.secondPing = this.loadPing(this.hostname, 1);
this.finished = true;
})
.catch(() => {
this.broken = true;
});
}
loadPing(hostname: string, loadNumber = 0) {
let timing = performance
.getEntriesByType("resource")
.filter(timing => timing.name.includes(hostname))
.filter(timing => timing.name.includes(`load=${loadNumber}`))[0];
// @ts-ignore
return Math.floor(timing.responseEnd - timing.startTime);
}
loadHostname(hostname: string, loadNumber = 0): Promise<string> {
return Axios.get(
`https://${hostname}/cdn-cgi/trace?load=${loadNumber}`
).then(response => {
return response.data
.split("\n")
.map((n: string) => n.split("="))
.filter((element: string[]) => element[0] == "colo")[0][1];
});
}
}
</script>

View file

@ -2,10 +2,14 @@
<div class="main-content"> <div class="main-content">
<div class="container has-text-centered"> <div class="container has-text-centered">
<h1 class="title">Cloudflare datacenter reachability</h1> <h1 class="title">Cloudflare datacenter reachability</h1>
<h2 class="subtitle"><a href="https://github.com/judge2020/cloudflare-connectivity-test/wiki/Explanation">Explanation</a></h2> <h2 class="subtitle">
<a
href="https://github.com/judge2020/cloudflare-connectivity-test/wiki/Explanation"
>Explanation</a>
</h2>
<p class="subtitle">Test your website:</p> <p class="subtitle">Test your website:</p>
<form @submit="loadTestHostname(testHostname)"> <form @submit.prevent="loadTestHostname(testHostname)">
<div class="field has-addons has-addons-centered"> <div class="field has-addons has-addons-centered">
<div class="control"> <div class="control">
<input <input
@ -13,7 +17,6 @@
type="text" type="text"
placeholder="example.com" placeholder="example.com"
v-model="testHostname" v-model="testHostname"
@blur="loadTestHostname(testHostname)"
/> />
</div> </div>
<div class="control"> <div class="control">
@ -22,15 +25,8 @@
</div> </div>
</form> </form>
<div class="domain-item" v-if="testHostname"> <div class="domain-item" v-if="testHostname" :key="testHostname">
<p class="heading" v-text="testHostname" /> <domain-item :hostname="testHostname" />
<div v-if="testHostname in finished">
<p class="title" v-text="finished[testHostname]" />
<p class="heading" v-text="iata[finished[testHostname]]" />
<p class="heading">First ping: {{ timingsFirst[testHostname] }}</p>
<p class="heading">Second ping: {{ timingsSecond[testHostname] }}</p>
</div>
<p v-show="testHostname in broken">Likely not a Cloudflare website!</p>
</div> </div>
<hr /> <hr />
</div> </div>
@ -39,53 +35,28 @@
<div class="column"> <div class="column">
<p class="title">FREE</p> <p class="title">FREE</p>
<div class="domain-item" v-for="(site, index) in free" :key="index"> <div class="domain-item" v-for="(site, index) in free" :key="index">
<p class="heading" v-text="site" /> <domain-item :hostname="site" />
<div v-if="site in finished">
<p class="title" v-text="finished[site]" />
<p class="heading" v-text="iata[finished[site]]" />
<p class="heading">First ping: {{ timingsFirst[site] }}</p>
<p class="heading">Second ping: {{ timingsSecond[site] }}</p>
</div>
<hr /> <hr />
</div> </div>
</div> </div>
<div class="column list-domains"> <div class="column list-domains">
<p class="title">PRO</p> <p class="title">PRO</p>
<div class="domain-item" v-for="(site, index) in pro" :key="index"> <div class="domain-item" v-for="(site, index) in pro" :key="index">
<p class="heading" v-text="site" /> <domain-item :hostname="site" />
<div v-if="site in finished">
<p class="title" v-text="finished[site]" />
<p class="heading" v-text="iata[finished[site]]" />
<p class="heading">First ping: {{ timingsFirst[site] }}</p>
<p class="heading">Second ping: {{ timingsSecond[site] }}</p>
</div>
<hr /> <hr />
</div> </div>
</div> </div>
<div class="column list-domains"> <div class="column list-domains">
<p class="title">BUSINESS</p> <p class="title">BUSINESS</p>
<div class="domain-item" v-for="(site, index) in business" :key="index"> <div class="domain-item" v-for="(site, index) in business" :key="index">
<p class="heading" v-text="site" /> <domain-item :hostname="site" />
<div v-if="site in finished">
<p class="title" v-text="finished[site]" />
<p class="heading" v-text="iata[finished[site]]" />
<p class="heading">First ping: {{ timingsFirst[site] }}</p>
<p class="heading">Second ping: {{ timingsSecond[site] }}</p>
</div>
<hr /> <hr />
</div> </div>
</div> </div>
<div class="column list-domains"> <div class="column list-domains">
<p class="title">ENTERPRISE</p> <p class="title">ENTERPRISE</p>
<div class="domain-item" v-for="(site, index) in enterprise" :key="index"> <div class="domain-item" v-for="(site, index) in enterprise" :key="index">
<p class="heading" v-text="site" /> <domain-item :hostname="site" />
<div v-if="site in finished">
<p class="title" v-text="finished[site]" />
<p class="heading" v-text="iata[finished[site]]" />
<p class="heading">First ping: {{ timingsFirst[site] }}</p>
<p class="heading">Second ping: {{ timingsSecond[site] }}</p>
</div>
<hr /> <hr />
</div> </div>
</div> </div>
@ -112,153 +83,89 @@
</template> </template>
<script> <script>
import Vue from "vue"; import { Vue, Component, Prop } from "vue-property-decorator";
import Axios from "axios"; import Axios from "axios";
export default { import DomainItem from "./DomainItem";
name: "Main", @Component({
props: {}, components: {
data() { DomainItem
return { }
testHostname: "", })
iata: [], export default class Main extends Vue {
timingsFirst: [], iata = [];
timingsSecond: [], free = [
finished: [], "judge2020.com",
broken: [], "domjh.net",
free: [ "digital.com",
"judge2020.com", "firing.it",
"domjh.net", "www.shoutmeloud.com",
"digital.com", "judge2020.me",
"firing.it", "cloudeereviews.com"
"www.shoutmeloud.com", ];
"judge2020.me", pro = [
"cloudeereviews.com" "js.org",
], "git-scm.com",
pro: [ "nodejs.org",
"js.org", "cdnjs.com",
"git-scm.com", "getbootstrap.com",
"nodejs.org", "reactjs.org",
"cdnjs.com", "html5boilerplate.com",
"getbootstrap.com", "yarnpkg.com",
"reactjs.org", "d3js.org"
"html5boilerplate.com", ];
"yarnpkg.com", business = [
"d3js.org" "judge.sh",
], "www.mozilla.org",
business: [ "domjh.com",
"judge.sh", "manfredi.io",
"www.mozilla.org", "corporateclash.net",
"domjh.com", "sontusdatos.org",
"manfredi.io", "www.opentech.fund",
"corporateclash.net", "cpj.org",
"sontusdatos.org", "www.amnestyusa.org",
"www.opentech.fund", "cdt.org",
"cpj.org", "www.counterextremism.com",
"www.amnestyusa.org", "www.ndi.org",
"cdt.org", "www.findlaw.com",
"www.counterextremism.com", "www.codeguard.com",
"www.ndi.org", "www.techagainstterrorism.org",
"www.findlaw.com", "www.thetrevorproject.org"
"www.codeguard.com", ];
"www.techagainstterrorism.org", enterprise = [
"www.thetrevorproject.org" "cloudflare.com",
], "cdnjs.cloudflare.com",
enterprise: [ "medium.com",
"cloudflare.com", "discordapp.com",
"cdnjs.cloudflare.com", "www.zendesk.com",
"medium.com", "ghost.io",
"discordapp.com", "unpkg.com",
"www.zendesk.com", "www.loc.gov",
"ghost.io", "www.artstation.com",
"unpkg.com", "www.digitalocean.com",
"www.loc.gov", "quizlet.com",
"www.artstation.com", "i.gyazo.com"
"www.digitalocean.com", ];
"quizlet.com",
"i.gyazo.com"
]
};
},
mounted() { mounted() {
this.preloadAirports(); this.preloadAirports();
this.free
.concat(this.pro, this.business, this.enterprise)
.forEach(hostname => {
this.loadHostname(hostname, 0)
.then(() => {
// load UI with ping data
this.loadPing(hostname, 0);
// begin promise for second ping (TCP and DNS overhead should be gone)
return this.loadHostname(hostname, 1);
})
.then(() => {
// load UI with second ping data
this.loadPing(hostname, 1);
});
});
// load query string hostname // load query string hostname
if (location.hash) { if (location.hash) {
this.testHostname = location.hash.replace("#", ""); this.testHostname = location.hash.replace("#", "");
this.loadTestHostname(this.testHostname); this.loadTestHostname(this.testHostname);
this.$forceUpdate(); this.$forceUpdate();
} }
},
methods: {
loadPing(hostname, loadNumber = 0) {
performance
.getEntriesByType("resource")
.filter(timing => timing.name.includes(hostname))
.filter(timing => timing.name.includes(`load=${loadNumber}`))
.forEach(timing => {
if (loadNumber == 0) {
this.timingsFirst[new URL(timing.name).hostname] = Math.floor(
timing.responseEnd - timing.startTime
);
} else {
this.timingsSecond[new URL(timing.name).hostname] = Math.floor(
timing.responseEnd - timing.startTime
);
}
});
this.$forceUpdate();
},
preloadAirports() {
Axios.get("/iata.json").then(response => {
this.iata = response.data;
});
},
loadTestHostname(hostname) {
history.replaceState(
"",
"",
hostname.includes("#") ? hostname : "#" + hostname
);
this.loadHostname(hostname, 0)
.then(() => {
this.loadPing(hostname, 0);
return this.loadHostname(hostname, 1);
})
.then(() => {
this.loadPing(hostname, 1);
});
},
loadHostname(hostname, loadNumber = 0) {
return Axios.get(
`https://${hostname}/cdn-cgi/trace?load=${loadNumber}`
).then(response => {
response.data
.split("\n")
.map(n => n.split("="))
.forEach(element => {
if (element[0] == "colo") {
Vue.set(this.finished, hostname, element[1]);
}
});
});
}
} }
}; loadTestHostname() {
this.$forceUpdate();
}
getAirport(code) {
return this.iata[code];
}
preloadAirports() {
Axios.get("/iata.json").then(response => {
this.iata = response.data;
});
}
}
</script> </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->

13
src/shims-tsx.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}

4
src/shims-vue.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}

39
tsconfig.json Normal file
View file

@ -0,0 +1,39 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}