big commit without a message
This commit is contained in:
parent
051d5b3c52
commit
d96fb38aa1
|
@ -11,8 +11,12 @@
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"cookie": "^0.4.0",
|
"cookie": "^0.4.0",
|
||||||
|
"echarts": "^5.1.2",
|
||||||
"hat": "^0.0.3",
|
"hat": "^0.0.3",
|
||||||
|
"json-bigint": "^1.0.0",
|
||||||
|
"nbt-ts": "^1.3.3",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
|
"node-gzip": "^1.1.2",
|
||||||
"polka": "^0.5.2",
|
"polka": "^0.5.2",
|
||||||
"sirv": "^1.0.0"
|
"sirv": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -34,7 +38,9 @@
|
||||||
"@types/compression": "^1.7.0",
|
"@types/compression": "^1.7.0",
|
||||||
"@types/cookie": "^0.4.0",
|
"@types/cookie": "^0.4.0",
|
||||||
"@types/hat": "^0.0.1",
|
"@types/hat": "^0.0.1",
|
||||||
|
"@types/json-bigint": "^1.0.1",
|
||||||
"@types/node": "^14.11.1",
|
"@types/node": "^14.11.1",
|
||||||
|
"@types/node-gzip": "^1.1.0",
|
||||||
"@types/polka": "^0.5.1",
|
"@types/polka": "^0.5.1",
|
||||||
"rollup": "^2.3.4",
|
"rollup": "^2.3.4",
|
||||||
"rollup-plugin-svelte": "^7.0.0",
|
"rollup-plugin-svelte": "^7.0.0",
|
||||||
|
@ -1864,6 +1870,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/json-bigint": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-zpchZLNsNuzJHi6v64UBoFWAvQlPhch7XAi36FkH6tL1bbbmimIF+cS7vwkzY4u5RaSWMoflQfu+TshMPPw8uw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
|
@ -1875,6 +1887,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz",
|
||||||
"integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A=="
|
"integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node-gzip": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-gzip/-/node-gzip-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-j7cGb6HIOZbDx3sqe9/9VAPeSvyt143yu5k35gzRXE3mxEgK6BOZ6BAiJ3ToXBcJqLzL9Cr53dav21jlp3f9gw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/polka": {
|
"node_modules/@types/polka": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/polka/-/polka-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/polka/-/polka-0.5.2.tgz",
|
||||||
|
@ -2095,6 +2116,14 @@
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bignumber.js": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
@ -2490,6 +2519,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/echarts": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-okUhO4sw22vwZp+rTPNjd/bvTdpug4K4sHNHyrV8NdAncIX9/AarlolFqtJCAYKGFYhUBNjIWu1EznFrSWTFxg==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "2.0.3",
|
||||||
|
"zrender": "5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/echarts/node_modules/tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.3.768",
|
"version": "1.3.768",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz",
|
||||||
|
@ -2947,6 +2990,14 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/json-bigint": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"bignumber.js": "^9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||||
|
@ -3169,6 +3220,11 @@
|
||||||
"node": ">=8.3.0"
|
"node": ">=8.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nbt-ts": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/nbt-ts/-/nbt-ts-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-xmEVWDJzO7YdA2YJHqAkfiOKlKECXe/hfNB10t3W7aDJsCXTjXyRbhP5HYvCwrMefGk8p6arQqeMO2V6djyfxQ=="
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
|
@ -3199,6 +3255,11 @@
|
||||||
"node": "4.x || >=6.0.0"
|
"node": "4.x || >=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-gzip": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw=="
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "1.1.73",
|
"version": "1.1.73",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
|
||||||
|
@ -4196,6 +4257,19 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
|
},
|
||||||
|
"node_modules/zrender": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-oeWlmUZPQdS9f5hK4pV21tHPqA3wgQ7CkKkw7l0CCBgWlJ/FP+lRgLFtUBW6yam4JX8y9CdHJo1o587VVrbcoQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zrender/node_modules/tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -5481,6 +5555,12 @@
|
||||||
"integrity": "sha512-GQoFDN07Knft7pvik72m/ddy8wmwMykzJEPZLoT7Wp3PHZsttdAbQ51qKf1DLWAdU6Ac31xon1Ji3g0hqQv6sw==",
|
"integrity": "sha512-GQoFDN07Knft7pvik72m/ddy8wmwMykzJEPZLoT7Wp3PHZsttdAbQ51qKf1DLWAdU6Ac31xon1Ji3g0hqQv6sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/json-bigint": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-zpchZLNsNuzJHi6v64UBoFWAvQlPhch7XAi36FkH6tL1bbbmimIF+cS7vwkzY4u5RaSWMoflQfu+TshMPPw8uw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/mime": {
|
"@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
|
@ -5492,6 +5572,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz",
|
||||||
"integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A=="
|
"integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A=="
|
||||||
},
|
},
|
||||||
|
"@types/node-gzip": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-gzip/-/node-gzip-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-j7cGb6HIOZbDx3sqe9/9VAPeSvyt143yu5k35gzRXE3mxEgK6BOZ6BAiJ3ToXBcJqLzL9Cr53dav21jlp3f9gw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/polka": {
|
"@types/polka": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/polka/-/polka-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/polka/-/polka-0.5.2.tgz",
|
||||||
|
@ -5681,6 +5770,11 @@
|
||||||
"node-addon-api": "^3.1.0"
|
"node-addon-api": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bignumber.js": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
|
||||||
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
@ -5988,6 +6082,22 @@
|
||||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||||
},
|
},
|
||||||
|
"echarts": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-okUhO4sw22vwZp+rTPNjd/bvTdpug4K4sHNHyrV8NdAncIX9/AarlolFqtJCAYKGFYhUBNjIWu1EznFrSWTFxg==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "2.0.3",
|
||||||
|
"zrender": "5.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.768",
|
"version": "1.3.768",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.768.tgz",
|
||||||
|
@ -6335,6 +6445,14 @@
|
||||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
|
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"json-bigint": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||||
|
"requires": {
|
||||||
|
"bignumber.js": "^9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||||
|
@ -6497,6 +6615,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/multi-part-lite/-/multi-part-lite-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/multi-part-lite/-/multi-part-lite-1.0.0.tgz",
|
||||||
"integrity": "sha512-KxIRbBZZ45hoKX1ROD/19wJr0ql1bef1rE8Y1PCwD3PuNXV42pp7Wo8lEHYuAajoT4vfAFcd3rPjlkyEEyt1nw=="
|
"integrity": "sha512-KxIRbBZZ45hoKX1ROD/19wJr0ql1bef1rE8Y1PCwD3PuNXV42pp7Wo8lEHYuAajoT4vfAFcd3rPjlkyEEyt1nw=="
|
||||||
},
|
},
|
||||||
|
"nbt-ts": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/nbt-ts/-/nbt-ts-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-xmEVWDJzO7YdA2YJHqAkfiOKlKECXe/hfNB10t3W7aDJsCXTjXyRbhP5HYvCwrMefGk8p6arQqeMO2V6djyfxQ=="
|
||||||
|
},
|
||||||
"negotiator": {
|
"negotiator": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
|
@ -6521,6 +6644,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||||
},
|
},
|
||||||
|
"node-gzip": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw=="
|
||||||
|
},
|
||||||
"node-releases": {
|
"node-releases": {
|
||||||
"version": "1.1.73",
|
"version": "1.1.73",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
|
||||||
|
@ -7259,6 +7387,21 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
|
},
|
||||||
|
"zrender": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-oeWlmUZPQdS9f5hK4pV21tHPqA3wgQ7CkKkw7l0CCBgWlJ/FP+lRgLFtUBW6yam4JX8y9CdHJo1o587VVrbcoQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "2.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,12 @@
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"cookie": "^0.4.0",
|
"cookie": "^0.4.0",
|
||||||
|
"echarts": "^5.1.2",
|
||||||
"hat": "^0.0.3",
|
"hat": "^0.0.3",
|
||||||
|
"json-bigint": "^1.0.0",
|
||||||
|
"nbt-ts": "^1.3.3",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
|
"node-gzip": "^1.1.2",
|
||||||
"polka": "^0.5.2",
|
"polka": "^0.5.2",
|
||||||
"sirv": "^1.0.0"
|
"sirv": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -37,7 +41,9 @@
|
||||||
"@types/compression": "^1.7.0",
|
"@types/compression": "^1.7.0",
|
||||||
"@types/cookie": "^0.4.0",
|
"@types/cookie": "^0.4.0",
|
||||||
"@types/hat": "^0.0.1",
|
"@types/hat": "^0.0.1",
|
||||||
|
"@types/json-bigint": "^1.0.1",
|
||||||
"@types/node": "^14.11.1",
|
"@types/node": "^14.11.1",
|
||||||
|
"@types/node-gzip": "^1.1.0",
|
||||||
"@types/polka": "^0.5.1",
|
"@types/polka": "^0.5.1",
|
||||||
"rollup": "^2.3.4",
|
"rollup": "^2.3.4",
|
||||||
"rollup-plugin-svelte": "^7.0.0",
|
"rollup-plugin-svelte": "^7.0.0",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
export let hrefPrefix = "";
|
||||||
import { login } from "./stores";
|
import { login } from "./stores";
|
||||||
import { getGraphs } from "../lib/api";
|
import { getGraphs } from "../lib/api";
|
||||||
|
|
||||||
|
@ -15,14 +16,10 @@
|
||||||
</span>
|
</span>
|
||||||
{:then graphs}
|
{:then graphs}
|
||||||
{#each graphs as graph}
|
{#each graphs as graph}
|
||||||
<a class="item" href="./graph/{graph.graph}">
|
<a class="item" href="{hrefPrefix}/graph/{graph.graph}">
|
||||||
<h2>{graph.data.name} <span>#{graph.graph}</span></h2>
|
<h2>{graph.data.name} <span>#{graph.graph}</span></h2>
|
||||||
<h3>
|
<p>{graph.data.style}</p>
|
||||||
Value:
|
<code>{graph.data.value}</code>
|
||||||
{#each graph.data.value as key}
|
|
||||||
<span>{key}</span>
|
|
||||||
{/each}
|
|
||||||
</h3>
|
|
||||||
</a>
|
</a>
|
||||||
{/each}
|
{/each}
|
||||||
<!-- svelte-ignore a11y-missing-content -->
|
<!-- svelte-ignore a11y-missing-content -->
|
||||||
|
@ -36,6 +33,7 @@
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use "../styles/GridList";
|
@use "../styles/GridList";
|
||||||
|
@use "../styles/GridItems";
|
||||||
a.item {
|
a.item {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: hsl(30, 20%, 90%);
|
color: hsl(30, 20%, 90%);
|
||||||
|
@ -51,16 +49,7 @@
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
}
|
}
|
||||||
h3 span {
|
.add {
|
||||||
font-weight: 400;
|
@include GridItems.add;
|
||||||
}
|
|
||||||
h3 span::after {
|
|
||||||
content: " > ";
|
|
||||||
font-size: 0.6em;
|
|
||||||
color: hsl(30, 5%, 60%);
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
h3 span:nth-last-child(1)::after {
|
|
||||||
content: "";
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,21 +1,48 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let segment: string | undefined;
|
export let segment: string | undefined;
|
||||||
import { postLogout } from "../lib/api";
|
import { getPermissions, postLogout } from "../lib/api";
|
||||||
|
import { checkPermissions, Permissions } from "../lib/permissions";
|
||||||
import { extraNav, login } from "./stores";
|
import { extraNav, login } from "./stores";
|
||||||
|
|
||||||
|
let profiles = false;
|
||||||
|
let users = false;
|
||||||
|
$: if ($login !== undefined) {
|
||||||
|
getPermissions($login?.token).then(permissions => {
|
||||||
|
if (checkPermissions([Permissions.VIEW_PROFILES], permissions)) {
|
||||||
|
profiles = true;
|
||||||
|
}
|
||||||
|
if (checkPermissions([Permissions.VIEW_USERS], permissions)) {
|
||||||
|
users = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<a href="/" class:active={segment === undefined || segment === "profile"}>
|
<a
|
||||||
|
href="/profiles"
|
||||||
|
class:active={segment === "profiles" || segment === "profile"}
|
||||||
|
class:invisible={!profiles}
|
||||||
|
>
|
||||||
<h3>Profiles</h3>
|
<h3>Profiles</h3>
|
||||||
</a>
|
</a>
|
||||||
<span class="sep" />
|
<span class="sep" />
|
||||||
<a
|
<a
|
||||||
href="/graphs"
|
href="/graphs"
|
||||||
class:active={segment === "graphs" || segment === "graph"}
|
class:active={segment === "graphs" || segment === "graph"}
|
||||||
|
class:invisible={!profiles}
|
||||||
>
|
>
|
||||||
<h3>Graphs</h3>
|
<h3>Graphs</h3>
|
||||||
</a>
|
</a>
|
||||||
<span class="sep" />
|
<span class="sep" />
|
||||||
|
<a
|
||||||
|
href="/users"
|
||||||
|
class:active={segment === "users" || segment === "user"}
|
||||||
|
class:invisible={!users}
|
||||||
|
>
|
||||||
|
<h3>Users</h3>
|
||||||
|
</a>
|
||||||
|
<span class="sep" />
|
||||||
{#if $extraNav !== undefined}
|
{#if $extraNav !== undefined}
|
||||||
<span class="extra">
|
<span class="extra">
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -128,4 +155,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.invisible,
|
||||||
|
.invisible + .sep {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -49,3 +49,28 @@ function createExtraNav() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const extraNav = createExtraNav();
|
export const extraNav = createExtraNav();
|
||||||
|
|
||||||
|
function createCache() {
|
||||||
|
const { subscribe, set, update } = writable<{
|
||||||
|
[key: string]: string;
|
||||||
|
}>({});
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
set: (key: string, value: string) =>
|
||||||
|
update(current => ({
|
||||||
|
...current,
|
||||||
|
[key]: value
|
||||||
|
})),
|
||||||
|
unset: (key: string) =>
|
||||||
|
update(current => {
|
||||||
|
delete current[key];
|
||||||
|
return current;
|
||||||
|
}),
|
||||||
|
clear: () => set({}),
|
||||||
|
update
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const playerCache = createCache();
|
||||||
|
export const profileCache = createCache();
|
||||||
|
|
|
@ -55,11 +55,33 @@ export async function getProfiles(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of skyblock profiles by player
|
* Get a list of skyblock profiles by player
|
||||||
|
* @param token Authorization token
|
||||||
* @param player Player's UUID
|
* @param player Player's UUID
|
||||||
* @returns A list of profiles
|
* @returns A list of profiles
|
||||||
*/
|
*/
|
||||||
export async function getPlayerProfiles(player: string): Promise<Profile[]> {
|
export async function getPlayerProfiles(
|
||||||
let response = await request(`/api/profiles/${player}`);
|
token: string,
|
||||||
|
player: string
|
||||||
|
): Promise<Profile[]> {
|
||||||
|
let response = await request(`/api/profiles/${player}`, {
|
||||||
|
headers: { Authorization: token }
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a palayer's profile cute name
|
||||||
|
* @param player Player's UUID
|
||||||
|
* @param profile Profile's UUID
|
||||||
|
* @returns Profile's cute name
|
||||||
|
*/
|
||||||
|
export async function getProfileCuteName(
|
||||||
|
player: string,
|
||||||
|
profile: string
|
||||||
|
): Promise<string> {
|
||||||
|
let response = await request(
|
||||||
|
`/api/profiles/${player}/${profile}/cute-name`
|
||||||
|
);
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,3 +141,57 @@ export async function putProfiles(
|
||||||
);
|
);
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get permissions of authenticated user
|
||||||
|
* @param token Authorization token
|
||||||
|
* @returns Array of permissions
|
||||||
|
*/
|
||||||
|
export async function getPermissions(token?: string): Promise<string[]> {
|
||||||
|
let response = await request(
|
||||||
|
"/api/permissions",
|
||||||
|
token ? { headers: { Authorization: token } } : {}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function putGraph(
|
||||||
|
token: string | undefined,
|
||||||
|
type: string,
|
||||||
|
name: string,
|
||||||
|
style: string,
|
||||||
|
value: string,
|
||||||
|
xAxisName: string = "Date & time",
|
||||||
|
xAxisType: string = "time",
|
||||||
|
yAxisName: string = "",
|
||||||
|
yAxisType: string = "value"
|
||||||
|
): Promise<void> {
|
||||||
|
let searchParams = new URLSearchParams({
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
style,
|
||||||
|
value,
|
||||||
|
x_axis_name: xAxisName,
|
||||||
|
x_axis_type: xAxisType,
|
||||||
|
y_axis_name: yAxisName,
|
||||||
|
y_axis_type: yAxisType
|
||||||
|
});
|
||||||
|
await request("/api/graphs?" + searchParams.toString(), {
|
||||||
|
method: "PUT",
|
||||||
|
...(token ? { headers: { Authorization: token } } : {})
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGraph(
|
||||||
|
token: string | undefined,
|
||||||
|
graph: string,
|
||||||
|
profile: string,
|
||||||
|
member: string
|
||||||
|
): Promise<[number, number][]> {
|
||||||
|
let response = await request(
|
||||||
|
`/api/graphs/${graph}/${profile}/${member}`,
|
||||||
|
token ? { headers: { Authorization: token } } : {}
|
||||||
|
);
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { Document } from "arangojs/documents";
|
||||||
import { compare } from "bcrypt";
|
import { compare } from "bcrypt";
|
||||||
import type { IncomingMessage, ServerResponse } from "http";
|
import type { IncomingMessage, ServerResponse } from "http";
|
||||||
import type { Session, User } from "../routes/api/_types";
|
import type { Session, User } from "../routes/api/_types";
|
||||||
|
import type { Profile } from "./hypixel";
|
||||||
import { checkPermissions } from "./permissions";
|
import { checkPermissions } from "./permissions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,7 +6,38 @@ export type HypixelResponse<T> = {
|
||||||
} & T;
|
} & T;
|
||||||
|
|
||||||
export const ENDPOINT = "https://api.hypixel.net/";
|
export const ENDPOINT = "https://api.hypixel.net/";
|
||||||
const request = requestWithDefaults(ENDPOINT);
|
|
||||||
|
// Request function with rate limit handling
|
||||||
|
const request: ReturnType<typeof requestWithDefaults> = (() => {
|
||||||
|
async function req(
|
||||||
|
endpoint: string,
|
||||||
|
init?: RequestInit
|
||||||
|
): Promise<Response> {
|
||||||
|
try {
|
||||||
|
let response = await requestWithDefaults(ENDPOINT)(endpoint, init);
|
||||||
|
console.log(
|
||||||
|
`Left: ${response.headers.get(
|
||||||
|
"ratelimit-remaining"
|
||||||
|
)}\tReset: ${response.headers.get("ratelimit-reset")}s`
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
let response: Response = e.response;
|
||||||
|
if (response.status === 429) {
|
||||||
|
const reset = response.headers.get("ratelimit-reset");
|
||||||
|
if (reset) {
|
||||||
|
console.log("Rate limited, waiting for", reset, "seconds");
|
||||||
|
await new Promise(resolve =>
|
||||||
|
setTimeout(resolve, (2 + parseInt(reset, 10)) * 1000)
|
||||||
|
);
|
||||||
|
return req(endpoint, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return req;
|
||||||
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Hypixel API key information
|
* Get Hypixel API key information
|
||||||
|
@ -38,6 +69,7 @@ export type Profile = {
|
||||||
cute_name: string;
|
cute_name: string;
|
||||||
banking: any | null;
|
banking: any | null;
|
||||||
game_mode: string | null;
|
game_mode: string | null;
|
||||||
|
timestamp: number;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Get Skyblock profiles by player
|
* Get Skyblock profiles by player
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
export enum Permissions {
|
export enum Permissions {
|
||||||
ALL = "*",
|
ALL = "*",
|
||||||
VIEW = "view",
|
|
||||||
|
|
||||||
|
VIEW_PROFILES = "view_profiles",
|
||||||
ADD_PROFILE = "add_profile",
|
ADD_PROFILE = "add_profile",
|
||||||
REMOVE_PROFILE = "remove_profile",
|
REMOVE_PROFILE = "remove_profile",
|
||||||
|
|
||||||
ADD_GRAPH = "add_graph",
|
ADD_GRAPH = "add_graph",
|
||||||
|
MANUAL_GRAPH = "manual_graph", // separate because allows for remote code execution
|
||||||
REMOVE_GRAPH = "remove_graph",
|
REMOVE_GRAPH = "remove_graph",
|
||||||
|
|
||||||
VIEW_USERS = "view_users",
|
VIEW_USERS = "view_users",
|
||||||
|
|
|
@ -1,16 +1,59 @@
|
||||||
import { getOnlineStatus, getProfileByUUID } from "./lib/hypixel";
|
import nbt from "nbt-ts";
|
||||||
|
import gzip from "node-gzip";
|
||||||
|
import { getOnlineStatus, getProfileByUUID, Profile } from "./lib/hypixel";
|
||||||
|
|
||||||
export async function log(key: string, profile: string) {
|
export async function log(key: string, profile: string) {
|
||||||
let r = await getProfileByUUID(key, profile);
|
let r = await getProfileByUUID(key, profile);
|
||||||
if (!r.success || !r.profile) throw new Error("No success from Hypixel");
|
if (!r.success || !r.profile) throw new Error("No success from Hypixel");
|
||||||
|
|
||||||
let d = r.profile;
|
let d: Profile = r.profile as any;
|
||||||
|
d.timestamp = Date.now();
|
||||||
|
|
||||||
for (const member in d.members) {
|
for (const member in d.members) {
|
||||||
let onlineStatus = await getOnlineStatus(key, member);
|
let onlineStatus = await getOnlineStatus(key, member);
|
||||||
if (onlineStatus.success && onlineStatus.session) {
|
if (onlineStatus.success && onlineStatus.session) {
|
||||||
d.members[member].online_status = onlineStatus.session;
|
d.members[member].online_status = onlineStatus.session;
|
||||||
|
d.members[member].online_status.timestamp = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
const inventories = [
|
||||||
|
"inv_armor",
|
||||||
|
"quiver",
|
||||||
|
"talisman_bag",
|
||||||
|
"backpack_icons",
|
||||||
|
"fishing_bag",
|
||||||
|
"ender_chest_contents",
|
||||||
|
"wardrobe_contents",
|
||||||
|
"potion_bag",
|
||||||
|
"personal_vault_contents",
|
||||||
|
"inv_contents",
|
||||||
|
"candy_inventory_contents"
|
||||||
|
];
|
||||||
|
if (d.members[member].backpack_contents) {
|
||||||
|
for (const backpack in d.members[member].backpack_contents) {
|
||||||
|
await decodeInventory(
|
||||||
|
d.members[member].backpack_contents[backpack]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (d.members[member].backpack_icons) {
|
||||||
|
for (const icon in d.members[member].backpack_icons) {
|
||||||
|
await decodeInventory(d.members[member].backpack_icons[icon]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const idx in inventories) {
|
||||||
|
const inv = inventories[idx];
|
||||||
|
await decodeInventory(d.members[member][inv]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function decodeInventory(inventory: any) {
|
||||||
|
if (inventory?.data) {
|
||||||
|
inventory.data = nbt.decode(
|
||||||
|
await gzip.ungzip(Buffer.from(inventory.data, "base64"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,7 +40,9 @@
|
||||||
padding-top: $navbar-padding;
|
padding-top: $navbar-padding;
|
||||||
min-height: calc(100% - #{$navbar-padding} - 0.75em);
|
min-height: calc(100% - #{$navbar-padding} - 0.75em);
|
||||||
}
|
}
|
||||||
:global(*) {
|
:global(button),
|
||||||
|
:global(input),
|
||||||
|
:global(textarea) {
|
||||||
font-family: "Fira Code", monospace;
|
font-family: "Fira Code", monospace;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -25,10 +25,26 @@ export type Tracker = Config<
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type Graph = Config<
|
// Graphs
|
||||||
"graph",
|
export type Axis = {
|
||||||
{
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BaseGraph<TS = string, T = any> = Config<
|
||||||
|
TS,
|
||||||
|
T & {
|
||||||
name: string;
|
name: string;
|
||||||
value: (string | number)[];
|
style: string;
|
||||||
|
x_axis?: Axis;
|
||||||
|
y_axis?: Axis;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type Graph = BaseGraph<
|
||||||
|
"manual_graph",
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
|
@ -2,18 +2,20 @@ import { aql } from "arangojs";
|
||||||
import type { Document } from "arangojs/documents";
|
import type { Document } from "arangojs/documents";
|
||||||
import type { IncomingMessage, ServerResponse } from "http";
|
import type { IncomingMessage, ServerResponse } from "http";
|
||||||
import { authorizeRequest } from "../../lib/butil";
|
import { authorizeRequest } from "../../lib/butil";
|
||||||
import { Permissions } from "../../lib/permissions";
|
import { checkPermissions, Permissions } from "../../lib/permissions";
|
||||||
import { db } from "../../server";
|
import { db } from "../../server";
|
||||||
import type { Graph } from "./_types";
|
import type { Graph } from "./_types";
|
||||||
|
|
||||||
export async function get(req: IncomingMessage, res: ServerResponse) {
|
export async function get(req: IncomingMessage, res: ServerResponse) {
|
||||||
const [_, exit] = await authorizeRequest(req, res, db, [Permissions.VIEW]);
|
const [_, exit] = await authorizeRequest(req, res, db, [
|
||||||
|
Permissions.VIEW_PROFILES
|
||||||
|
]);
|
||||||
if (exit) return;
|
if (exit) return;
|
||||||
|
|
||||||
let graphs: (Graph & { graph: string })[] = [];
|
let graphs: (Graph & { graph: string })[] = [];
|
||||||
const cursor = await db.query(aql`
|
const cursor = await db.query(aql`
|
||||||
FOR doc IN config
|
FOR doc IN config
|
||||||
FILTER doc.type == "graph"
|
FILTER CONTAINS(doc.type, "graph")
|
||||||
RETURN doc
|
RETURN doc
|
||||||
`);
|
`);
|
||||||
while (cursor.hasNext) {
|
while (cursor.hasNext) {
|
||||||
|
@ -27,3 +29,93 @@ export async function get(req: IncomingMessage, res: ServerResponse) {
|
||||||
res.writeHead(200);
|
res.writeHead(200);
|
||||||
res.end(JSON.stringify(graphs));
|
res.end(JSON.stringify(graphs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function put(req: IncomingMessage, res: ServerResponse) {
|
||||||
|
const [user, exit] = await authorizeRequest(req, res, db, [
|
||||||
|
Permissions.ADD_GRAPH
|
||||||
|
]);
|
||||||
|
if (exit) return;
|
||||||
|
|
||||||
|
const url = new URL(req.url!, "https://example.com/");
|
||||||
|
if (!url.searchParams.get("name")) {
|
||||||
|
res.writeHead(400);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "name query parameter should be set"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!url.searchParams.get("style")) {
|
||||||
|
res.writeHead(400);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "style query parameter should be set"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!url.searchParams.get("type")) {
|
||||||
|
res.writeHead(400);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "type query parameter should be set"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (url.searchParams.get("type")) {
|
||||||
|
case "manual_graph":
|
||||||
|
if (
|
||||||
|
!checkPermissions([Permissions.MANUAL_GRAPH], user!.permissions)
|
||||||
|
) {
|
||||||
|
res.writeHead(403);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "You are unauthroized to make manual graphs"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!url.searchParams.get("value")) {
|
||||||
|
res.writeHead(400);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "value query parameter should be set"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await db.collection("config").save({
|
||||||
|
type: "manual_graph",
|
||||||
|
data: {
|
||||||
|
name: url.searchParams.get("name"),
|
||||||
|
style: url.searchParams.get("style"),
|
||||||
|
value: url.searchParams.get("value"),
|
||||||
|
x_axis: {
|
||||||
|
name:
|
||||||
|
url.searchParams.get("x_axis_name") ||
|
||||||
|
"Date & time",
|
||||||
|
type: url.searchParams.get("x_axis_type") || "time"
|
||||||
|
},
|
||||||
|
y_axis: {
|
||||||
|
name: url.searchParams.get("y_axis_name") || undefined,
|
||||||
|
type: url.searchParams.get("y_axis_type") || "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as Graph);
|
||||||
|
res.writeHead(204);
|
||||||
|
res.end();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res.writeHead(400);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "Invalid graph type"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import type { IncomingMessage, ServerResponse } from "http";
|
||||||
|
import { authorizeRequest } from "../../../../../lib/butil";
|
||||||
|
import { Permissions } from "../../../../../lib/permissions";
|
||||||
|
import { db } from "../../../../../server";
|
||||||
|
import type { Graph } from "../../../_types";
|
||||||
|
|
||||||
|
export async function get(
|
||||||
|
req: IncomingMessage & {
|
||||||
|
params: { graph: string; profile: string; player: string };
|
||||||
|
},
|
||||||
|
res: ServerResponse
|
||||||
|
) {
|
||||||
|
const [_, exit] = await authorizeRequest(req, res, db, [
|
||||||
|
Permissions.VIEW_PROFILES
|
||||||
|
]);
|
||||||
|
if (exit) return;
|
||||||
|
|
||||||
|
const cnf = db.collection("config");
|
||||||
|
const profile = db.collection("c" + req.params.profile);
|
||||||
|
if (!(await profile.exists())) {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "Profile not found"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let graph: Graph = await cnf.document(req.params.graph);
|
||||||
|
switch (graph.type) {
|
||||||
|
case "manual_graph":
|
||||||
|
let data = await (
|
||||||
|
await db.query({
|
||||||
|
query: `FOR profile IN ${profile.name}
|
||||||
|
LET member = profile.members[@member_uuid]
|
||||||
|
${graph.data.value}`,
|
||||||
|
bindVars: {
|
||||||
|
member_uuid: req.params.player
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).all();
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(data));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "Not a graph ID"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import type { IncomingMessage, ServerResponse } from "http";
|
||||||
|
import { authorizeRequest } from "../../lib/butil";
|
||||||
|
import { db } from "../../server";
|
||||||
|
|
||||||
|
export async function get(req: IncomingMessage, res: ServerResponse) {
|
||||||
|
const [user, exit] = await authorizeRequest(req, res, db, []);
|
||||||
|
if (exit) return;
|
||||||
|
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(user!.permissions));
|
||||||
|
}
|
|
@ -8,7 +8,9 @@ import { db, API_KEY } from "../../server";
|
||||||
import type { Tracker } from "./_types";
|
import type { Tracker } from "./_types";
|
||||||
|
|
||||||
export async function get(req: IncomingMessage, res: ServerResponse) {
|
export async function get(req: IncomingMessage, res: ServerResponse) {
|
||||||
const [_, exit] = await authorizeRequest(req, res, db, [Permissions.VIEW]);
|
const [_, exit] = await authorizeRequest(req, res, db, [
|
||||||
|
Permissions.VIEW_PROFILES
|
||||||
|
]);
|
||||||
if (exit) return;
|
if (exit) return;
|
||||||
|
|
||||||
let profiles: (Tracker & { profile: string })[] = [];
|
let profiles: (Tracker & { profile: string })[] = [];
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
import type { IncomingMessage, ServerResponse } from "http";
|
import type { IncomingMessage, ServerResponse } from "http";
|
||||||
|
import { authorizeRequest } from "../../../lib/butil";
|
||||||
|
import { Permissions } from "../../../lib/permissions";
|
||||||
import { getProfilesByPlayer } from "../../../lib/hypixel";
|
import { getProfilesByPlayer } from "../../../lib/hypixel";
|
||||||
import { API_KEY } from "../../../server";
|
import { API_KEY, db } from "../../../server";
|
||||||
|
|
||||||
export async function get(
|
export async function get(
|
||||||
req: IncomingMessage & { params: { player: string } },
|
req: IncomingMessage & { params: { player: string } },
|
||||||
res: ServerResponse
|
res: ServerResponse
|
||||||
) {
|
) {
|
||||||
|
const [_, exit] = await authorizeRequest(req, res, db, [
|
||||||
|
Permissions.ADD_PROFILE
|
||||||
|
]);
|
||||||
|
if (exit) return;
|
||||||
|
|
||||||
let response = await getProfilesByPlayer(API_KEY || "", req.params.player);
|
let response = await getProfilesByPlayer(API_KEY || "", req.params.player);
|
||||||
if (response.success && response.profiles !== undefined) {
|
if (response.success && response.profiles !== undefined) {
|
||||||
res.writeHead(200);
|
res.writeHead(200);
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import type { IncomingMessage, ServerResponse } from "http";
|
||||||
|
import { getProfilesByPlayer } from "../../../../../lib/hypixel";
|
||||||
|
import { API_KEY, profileNameCache } from "../../../../../server";
|
||||||
|
|
||||||
|
export async function get(
|
||||||
|
req: IncomingMessage & { params: { player: string; profile: string } },
|
||||||
|
res: ServerResponse
|
||||||
|
) {
|
||||||
|
if (profileNameCache[req.params.player]?.[req.params.profile]) {
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify(
|
||||||
|
profileNameCache[req.params.player]?.[req.params.profile]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = await getProfilesByPlayer(API_KEY || "", req.params.player);
|
||||||
|
if (response.success && response.profiles !== undefined) {
|
||||||
|
if (profileNameCache[req.params.player] === undefined) {
|
||||||
|
profileNameCache[req.params.player] = {};
|
||||||
|
}
|
||||||
|
response.profiles.forEach(
|
||||||
|
p =>
|
||||||
|
(profileNameCache[req.params.player][p.profile_id] =
|
||||||
|
p.cute_name)
|
||||||
|
);
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(
|
||||||
|
response.profiles.find(p => p.profile_id === req.params.profile)
|
||||||
|
?.cute_name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res.writeHead(response._response.status);
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
message: "Unsuccessful Hypixel API response"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from "@sapper/app";
|
||||||
|
import { login, extraNav } from "../../components/stores";
|
||||||
|
import { putGraph } from "../../lib/api";
|
||||||
|
|
||||||
|
$extraNav; // important for subscription to store to be activated
|
||||||
|
extraNav.set([["Add", () => (type = "")]]);
|
||||||
|
|
||||||
|
let type = "";
|
||||||
|
let name = "";
|
||||||
|
let style = "";
|
||||||
|
let value = "";
|
||||||
|
let status = "";
|
||||||
|
|
||||||
|
let xAxisName = "Date & time";
|
||||||
|
let yAxisName = "";
|
||||||
|
let xAxisType = "time";
|
||||||
|
let yAxisType = "value";
|
||||||
|
|
||||||
|
$: if (type !== "") {
|
||||||
|
extraNav.set([
|
||||||
|
["Add", () => (type = "")],
|
||||||
|
{
|
||||||
|
manual_graph: "Manual Graph"
|
||||||
|
}[type]!
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
extraNav.set([["Add", () => (type = "")]]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Add Graph - Skyblock Data Tracker</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div id="list">
|
||||||
|
{#if type === ""}
|
||||||
|
<button class="item" on:click={() => (type = "manual_graph")}>
|
||||||
|
<h1>Manual Graph</h1>
|
||||||
|
</button>
|
||||||
|
{:else if type === "manual_graph"}
|
||||||
|
<form
|
||||||
|
class="item"
|
||||||
|
on:submit={async event => {
|
||||||
|
event.preventDefault();
|
||||||
|
try {
|
||||||
|
await putGraph($login?.token, type, name, style, value);
|
||||||
|
goto("/graphs");
|
||||||
|
} catch (e) {
|
||||||
|
if (
|
||||||
|
e.response?.status === 401 ||
|
||||||
|
e.response?.status === 403
|
||||||
|
) {
|
||||||
|
alert("Unauthorised");
|
||||||
|
} else {
|
||||||
|
alert("An error occured while adding the profile");
|
||||||
|
}
|
||||||
|
status = "Error";
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h1>Manual Graph</h1>
|
||||||
|
<p id="status">{status}</p>
|
||||||
|
<input type="text" placeholder="Name" bind:value={name} />
|
||||||
|
<input type="text" placeholder="Style" bind:value={style} />
|
||||||
|
<textarea rows="16" placeholder="AQL" bind:value />
|
||||||
|
<h2>X Axis</h2>
|
||||||
|
<p>
|
||||||
|
Name: <input type="text" bind:value={xAxisName} />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Type: <input type="text" bind:value={xAxisType} />
|
||||||
|
</p>
|
||||||
|
<h2>Y Axis</h2>
|
||||||
|
<p>
|
||||||
|
Name: <input type="text" bind:value={yAxisName} />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Type: <input type="text" bind:value={yAxisType} />
|
||||||
|
</p>
|
||||||
|
<input type="submit" value="Add" />
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use "../../styles/GridList";
|
||||||
|
@use "../../styles/CodeForm";
|
||||||
|
|
||||||
|
.item {
|
||||||
|
color: hsl(30, 20%, 90%);
|
||||||
|
font-size: 1.17em;
|
||||||
|
}
|
||||||
|
button.item:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,70 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { login } from "../components/stores";
|
import { goto } from "@sapper/app";
|
||||||
import { getProfiles, getUsername } from "../lib/api";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
let profilesPromise: ReturnType<typeof getProfiles> = new Promise(() => {});
|
onMount(() => goto("/profiles"));
|
||||||
$: if ($login !== undefined) {
|
|
||||||
profilesPromise = getProfiles($login?.token);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Skyblock Data Tracker</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<div id="list">
|
|
||||||
{#await profilesPromise}
|
|
||||||
<div class="item">
|
|
||||||
<h2>Fetching profiles...</h2>
|
|
||||||
</div>
|
|
||||||
{:then profiles}
|
|
||||||
{#each profiles as profile}
|
|
||||||
<button type="button" class="item">
|
|
||||||
{#each profile.data.members as member, idx}
|
|
||||||
{#await getUsername(member)}
|
|
||||||
<a href="/profile/{profile.profile}/member/{member}"
|
|
||||||
>...</a
|
|
||||||
>
|
|
||||||
{:then username}
|
|
||||||
<a href="/profile/{profile.profile}/member/{member}">
|
|
||||||
{username}
|
|
||||||
{#if idx === 0 && profile.data.name}
|
|
||||||
@ {profile.data.name}
|
|
||||||
{/if}
|
|
||||||
</a>
|
|
||||||
{:catch error}
|
|
||||||
<p style="color: red">{error.message}</p>
|
|
||||||
{/await}
|
|
||||||
{/each}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
<!-- svelte-ignore a11y-missing-content -->
|
|
||||||
<a class="item add" href="/profile/new" />
|
|
||||||
{:catch error}
|
|
||||||
<div class="item">
|
|
||||||
<h2 style="color: red">{error.message}</h2>
|
|
||||||
</div>
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@use "../styles/GridList";
|
|
||||||
.item {
|
|
||||||
font-size: 1.17em;
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
margin: 1em 0;
|
|
||||||
color: hsl(30, 20%, 90%);
|
|
||||||
text-decoration: none;
|
|
||||||
font-family: "Minecraft";
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a:nth-child(1) {
|
|
||||||
color: hsl(60, 100%, 50%);
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -12,4 +12,26 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let profile: string;
|
export let profile: string;
|
||||||
export let member: string;
|
export let member: string;
|
||||||
|
import Graphs from "../../../../components/Graphs.svelte";
|
||||||
|
import { goto } from "@sapper/app";
|
||||||
|
import { extraNav, login } from "../../../../components/stores";
|
||||||
|
import { getPlayerProfiles, getUsername } from "../../../../lib/api";
|
||||||
|
|
||||||
|
$extraNav;
|
||||||
|
$: if ($login !== undefined) {
|
||||||
|
(async () => {
|
||||||
|
let username = await getUsername(member);
|
||||||
|
let profiles = await getPlayerProfiles(member);
|
||||||
|
extraNav.set([
|
||||||
|
[
|
||||||
|
username +
|
||||||
|
" @ " +
|
||||||
|
profiles.find(p => p.profile_id === profile)?.cute_name,
|
||||||
|
() => goto(`/profile/${profile}/member/${member}`)
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
})();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<Graphs hrefPrefix="/profile/{profile}/member/{member}" />
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<script context="module" lang="ts">
|
||||||
|
import type common from "@sapper/common";
|
||||||
|
|
||||||
|
export async function preload(page: common.Page) {
|
||||||
|
return {
|
||||||
|
profile: page.params.profile,
|
||||||
|
member: page.params.member,
|
||||||
|
graph: page.params.graph
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export let profile: string;
|
||||||
|
export let member: string;
|
||||||
|
export let graph: string;
|
||||||
|
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { init } from "echarts";
|
||||||
|
import { getGraph, getGraphs } from "../../../../../../lib/api";
|
||||||
|
import { login } from "../../../../../../components/stores";
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
let graphs = await getGraphs($login?.token);
|
||||||
|
let g = graphs.find(g => g.graph === graph);
|
||||||
|
|
||||||
|
let data = await getGraph($login?.token, graph, profile, member);
|
||||||
|
|
||||||
|
let c = document.querySelector("div#list");
|
||||||
|
if (c && g) {
|
||||||
|
init(c as HTMLElement, "dark").setOption({
|
||||||
|
title: {
|
||||||
|
text: g.data.name
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis"
|
||||||
|
},
|
||||||
|
xAxis: g.data.x_axis || {
|
||||||
|
name: "Date & time",
|
||||||
|
type: "time"
|
||||||
|
},
|
||||||
|
yAxis: g.data.y_axis || {
|
||||||
|
type: "value"
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: g.data.name,
|
||||||
|
type: g.data.style,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
],
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
id: "dataZoomX",
|
||||||
|
type: "slider",
|
||||||
|
filterMode: "filter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "dataZoomY",
|
||||||
|
type: "slider",
|
||||||
|
filterMode: "empty"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="list">
|
||||||
|
<div class="item"><h1>Loading...</h1></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use "../../../../../../styles/GridList";
|
||||||
|
</style>
|
|
@ -85,7 +85,7 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
goto("/#");
|
goto("/profiles");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#await getUsername(uuid)}
|
{#await getUsername(uuid)}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { login } from "../components/stores";
|
||||||
|
import { getProfiles, getUsername } from "../lib/api";
|
||||||
|
|
||||||
|
let profilesPromise: ReturnType<typeof getProfiles> = new Promise(() => {});
|
||||||
|
$: if ($login !== undefined) {
|
||||||
|
profilesPromise = getProfiles($login?.token);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Skyblock Data Tracker</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div id="list">
|
||||||
|
{#await profilesPromise}
|
||||||
|
<div class="item">
|
||||||
|
<h2>Fetching profiles...</h2>
|
||||||
|
</div>
|
||||||
|
{:then profiles}
|
||||||
|
{#each profiles as profile}
|
||||||
|
<button type="button" class="item">
|
||||||
|
{#each profile.data.members as member, idx}
|
||||||
|
{#await getUsername(member)}
|
||||||
|
<a href="/profile/{profile.profile}/member/{member}"
|
||||||
|
>...</a
|
||||||
|
>
|
||||||
|
{:then username}
|
||||||
|
<a href="/profile/{profile.profile}/member/{member}">
|
||||||
|
{username}
|
||||||
|
{#if idx === 0 && profile.data.name}
|
||||||
|
@ {profile.data.name}
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
{:catch error}
|
||||||
|
<p style="color: red">{error.message}</p>
|
||||||
|
{/await}
|
||||||
|
{/each}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
<!-- svelte-ignore a11y-missing-content -->
|
||||||
|
<a class="item add" href="/profile/new" />
|
||||||
|
{:catch error}
|
||||||
|
<div class="item">
|
||||||
|
<h2 style="color: red">{error.message}</h2>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use "../styles/GridList";
|
||||||
|
@use "../styles/GridItems";
|
||||||
|
|
||||||
|
.item {
|
||||||
|
font-size: 1.17em;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
margin: 1em 0;
|
||||||
|
color: hsl(30, 20%, 90%);
|
||||||
|
text-decoration: none;
|
||||||
|
font-family: "Minecraft";
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a:nth-child(1) {
|
||||||
|
color: hsl(60, 100%, 50%);
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.add {
|
||||||
|
@include GridItems.add;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -7,6 +7,16 @@ import arangojs, { Database, aql } from "arangojs";
|
||||||
import { hash } from "bcrypt";
|
import { hash } from "bcrypt";
|
||||||
import { log } from "./logger";
|
import { log } from "./logger";
|
||||||
import { Permissions } from "./lib/permissions";
|
import { Permissions } from "./lib/permissions";
|
||||||
|
import { stringify } from "json-bigint";
|
||||||
|
|
||||||
|
// HACK : BigInt support on JSON.stringify
|
||||||
|
JSON.stringify = stringify;
|
||||||
|
|
||||||
|
export let profileNameCache: {
|
||||||
|
[player: string]: {
|
||||||
|
[profile: string]: string;
|
||||||
|
};
|
||||||
|
} = {};
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
PORT,
|
PORT,
|
||||||
|
@ -71,7 +81,7 @@ const intervalFunc = async (db: Database) => {
|
||||||
});
|
});
|
||||||
await users.save({
|
await users.save({
|
||||||
username: "_guest",
|
username: "_guest",
|
||||||
permissions: [Permissions.VIEW]
|
permissions: [Permissions.VIEW_PROFILES]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!(await sessions.exists())) {
|
if (!(await sessions.exists())) {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
@use "./Form";
|
||||||
|
textarea {
|
||||||
|
@include Form.input;
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ form {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
@mixin input {
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0.75em;
|
padding: 0.75em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
@ -22,6 +22,9 @@ input {
|
||||||
border-color: black;
|
border-color: black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
input {
|
||||||
|
@include input;
|
||||||
|
}
|
||||||
input[type="submit"] {
|
input[type="submit"] {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
@mixin add {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: min(12em, 30%);
|
||||||
|
height: 0.3em;
|
||||||
|
background-color: hsl(30, 20%, 90%);
|
||||||
|
}
|
||||||
|
&::before {
|
||||||
|
transform: translate(-50%, -50%) rotateZ(90deg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,21 +22,3 @@ div#list {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.add {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: min(12em, 30%);
|
|
||||||
height: 0.3em;
|
|
||||||
background-color: hsl(30, 20%, 90%);
|
|
||||||
}
|
|
||||||
&::before {
|
|
||||||
transform: translate(-50%, -50%) rotateZ(90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue