├── src ├── boot │ ├── .gitkeep │ ├── axios.js │ ├── i18n.js │ └── logger.js ├── store │ ├── module-example │ │ ├── getters.js │ │ ├── actions.js │ │ ├── mutations.js │ │ ├── state.js │ │ └── index.js │ ├── store-flag.d.ts │ └── index.js ├── i18n │ ├── index.js │ └── en-US │ │ └── index.js ├── css │ ├── app.scss │ └── quasar.variables.scss ├── App.vue ├── router │ ├── routes.js │ └── index.js ├── pages │ ├── Error404.vue │ ├── Index.vue │ └── VnDashboard.vue ├── layouts │ └── MainLayout.vue ├── index.template.html ├── utils │ ├── vn_chart.js │ ├── vn_utils.js │ └── chart_utils.js ├── components │ └── VnChart.vue └── assets │ └── quasar-logo-vertical.svg ├── docs ├── demo.png └── demo.json ├── public ├── favicon.ico └── icons │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ └── favicon-128x128.png ├── .eslintignore ├── babel.config.js ├── .editorconfig ├── .postcssrc.js ├── .vscode ├── settings.json └── extensions.json ├── .gitignore ├── jsconfig.json ├── README.md ├── .github └── workflows │ ├── gh-page.yml │ └── release.yml ├── package.json ├── .eslintrc.js └── quasar.conf.js /src/boot/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITII/VNSTAT-DASHBOARD/HEAD/docs/demo.png -------------------------------------------------------------------------------- /src/store/module-example/getters.js: -------------------------------------------------------------------------------- 1 | export function someGetter (/* state */) { 2 | } 3 | -------------------------------------------------------------------------------- /src/store/module-example/actions.js: -------------------------------------------------------------------------------- 1 | export function someAction (/* context */) { 2 | } 3 | -------------------------------------------------------------------------------- /src/store/module-example/mutations.js: -------------------------------------------------------------------------------- 1 | export function someMutation (/* state */) { 2 | } 3 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITII/VNSTAT-DASHBOARD/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import enUS from './en-US' 2 | 3 | export default { 4 | 'en-US': enUS 5 | } 6 | -------------------------------------------------------------------------------- /src/store/module-example/state.js: -------------------------------------------------------------------------------- 1 | export default function () { 2 | return { 3 | // 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/boot/axios.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import axios from 'axios' 3 | 4 | Vue.prototype.$axios = axios 5 | -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITII/VNSTAT-DASHBOARD/HEAD/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITII/VNSTAT-DASHBOARD/HEAD/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITII/VNSTAT-DASHBOARD/HEAD/public/icons/favicon-96x96.png -------------------------------------------------------------------------------- /public/icons/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IITII/VNSTAT-DASHBOARD/HEAD/public/icons/favicon-128x128.png -------------------------------------------------------------------------------- /src/css/app.scss: -------------------------------------------------------------------------------- 1 | // app global css in SCSS format 2 | .eighty-width { 3 | width: 80%; 4 | //min-width: 600px; 5 | } 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /src-bex/www 3 | /src-capacitor 4 | /src-cordova 5 | /.quasar 6 | /node_modules 7 | .eslintrc.js 8 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | presets: [ 5 | '@quasar/babel-preset-app' 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/i18n/en-US/index.js: -------------------------------------------------------------------------------- 1 | // This is just an example, 2 | // so you can safely delete all default props below 3 | 4 | export default { 5 | failed: 'Action failed', 6 | success: 'Action was successful' 7 | } 8 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vetur.validation.template": false, 3 | "vetur.format.enable": false, 4 | "eslint.validate": [ 5 | "javascript", 6 | "javascriptreact", 7 | "typescript", 8 | "vue" 9 | ], 10 | "vetur.experimental.templateInterpolationService": true 11 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "octref.vetur" 6 | ], 7 | "unwantedRecommendations": [ 8 | "hookyqr.beautify", 9 | "dbaeumer.jshint", 10 | "ms-vscode.vscode-typescript-tslint-plugin" 11 | ] 12 | } -------------------------------------------------------------------------------- /src/store/module-example/index.js: -------------------------------------------------------------------------------- 1 | import state from './state' 2 | import * as getters from './getters' 3 | import * as mutations from './mutations' 4 | import * as actions from './actions' 5 | 6 | export default { 7 | namespaced: true, 8 | getters, 9 | mutations, 10 | actions, 11 | state 12 | } 13 | -------------------------------------------------------------------------------- /src/store/store-flag.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED, 3 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING 4 | import "quasar/dist/types/feature-flag"; 5 | 6 | declare module "quasar/dist/types/feature-flag" { 7 | interface QuasarFeatureFlags { 8 | store: true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/boot/i18n.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import messages from 'src/i18n' 4 | 5 | Vue.use(VueI18n) 6 | 7 | const i18n = new VueI18n({ 8 | locale: 'en-us', 9 | fallbackLocale: 'en-us', 10 | messages 11 | }) 12 | 13 | export default ({ app }) => { 14 | // Set i18n instance on app 15 | app.i18n = i18n 16 | } 17 | 18 | export { i18n } 19 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | 2 | const routes = [ 3 | { 4 | path: '/', 5 | component: () => import('layouts/MainLayout.vue'), 6 | children: [ 7 | { path: '', component: () => import('pages/Index.vue') } 8 | ] 9 | }, 10 | 11 | // Always leave this as last one, 12 | // but you can also remove it 13 | { 14 | path: '*', 15 | component: () => import('pages/Error404.vue') 16 | } 17 | ] 18 | 19 | export default routes 20 | -------------------------------------------------------------------------------- /src/boot/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author IITII 3 | * @date 2022/08/10 4 | */ 5 | 'use strict' 6 | import Vue from 'vue' 7 | import VueLogger from 'vuejs-logger' 8 | 9 | const isProduction = process.env.NODE_ENV === 'production' 10 | 11 | const options = { 12 | isEnabled: true, 13 | logLevel: isProduction ? 'error' : 'debug', 14 | stringifyArguments: false, 15 | showLogLevel: true, 16 | showMethodName: true, 17 | separator: '|', 18 | showConsoleColors: true 19 | } 20 | 21 | Vue.use(VueLogger, options) 22 | -------------------------------------------------------------------------------- /src/pages/Error404.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 6 | 7 | 8 | 9 | Oops. Nothing here... 10 | 11 | 12 | 21 | 22 | 23 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/layouts/MainLayout.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | VnStat Dashboard 9 | 10 | 11 | Github@IITII 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .thumbs.db 3 | node_modules 4 | package-lock.json 5 | yarn.lock 6 | 7 | # Quasar core related directories 8 | .quasar 9 | /dist 10 | 11 | # Cordova related directories and files 12 | /src-cordova/node_modules 13 | /src-cordova/platforms 14 | /src-cordova/plugins 15 | /src-cordova/www 16 | 17 | # Capacitor related directories and files 18 | /src-capacitor/www 19 | /src-capacitor/node_modules 20 | 21 | # BEX related directories and files 22 | /src-bex/www 23 | /src-bex/js/core 24 | 25 | # Log files 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # Editor directories and files 31 | .idea 32 | *.suo 33 | *.ntvs* 34 | *.njsproj 35 | *.sln 36 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "src/*": [ 6 | "src/*" 7 | ], 8 | "app/*": [ 9 | "*" 10 | ], 11 | "components/*": [ 12 | "src/components/*" 13 | ], 14 | "layouts/*": [ 15 | "src/layouts/*" 16 | ], 17 | "pages/*": [ 18 | "src/pages/*" 19 | ], 20 | "assets/*": [ 21 | "src/assets/*" 22 | ], 23 | "boot/*": [ 24 | "src/boot/*" 25 | ], 26 | "vue$": [ 27 | "node_modules/vue/dist/vue.esm.js" 28 | ] 29 | } 30 | }, 31 | "exclude": [ 32 | "dist", 33 | ".quasar", 34 | "node_modules" 35 | ] 36 | } -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | // import example from './module-example' 5 | 6 | Vue.use(Vuex) 7 | 8 | /* 9 | * If not building with SSR mode, you can 10 | * directly export the Store instantiation; 11 | * 12 | * The function below can be async too; either use 13 | * async/await or return a Promise which resolves 14 | * with the Store instance. 15 | */ 16 | 17 | export default function (/* { ssrContext } */) { 18 | const Store = new Vuex.Store({ 19 | modules: { 20 | // example 21 | }, 22 | 23 | // enable strict mode (adds overhead!) 24 | // for dev mode only 25 | strict: process.env.DEBUGGING 26 | }) 27 | 28 | return Store 29 | } 30 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import routes from './routes' 5 | 6 | Vue.use(VueRouter) 7 | 8 | /* 9 | * If not building with SSR mode, you can 10 | * directly export the Router instantiation; 11 | * 12 | * The function below can be async too; either use 13 | * async/await or return a Promise which resolves 14 | * with the Router instance. 15 | */ 16 | 17 | export default function (/* { store, ssrContext } */) { 18 | const Router = new VueRouter({ 19 | scrollBehavior: () => ({ x: 0, y: 0 }), 20 | routes, 21 | 22 | // Leave these as they are and change in quasar.conf.js instead! 23 | // quasar.conf.js -> build -> vueRouterMode 24 | // quasar.conf.js -> build -> publicPath 25 | mode: process.env.VUE_ROUTER_MODE, 26 | base: process.env.VUE_ROUTER_BASE 27 | }) 28 | 29 | return Router 30 | } 31 | -------------------------------------------------------------------------------- /src/css/quasar.variables.scss: -------------------------------------------------------------------------------- 1 | // Quasar SCSS (& Sass) Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Sass/SCSS variables found in Quasar's source Sass/SCSS files. 5 | 6 | // Check documentation for full list of Quasar variables 7 | 8 | // Your own variables (that are declared here) and Quasar's own 9 | // ones will be available out of the box in your .vue/.scss/.sass files 10 | 11 | // It's highly recommended to change the default colors 12 | // to match your app's branding. 13 | // Tip: Use the "Theme Builder" on Quasar's documentation website. 14 | 15 | $primary : #1976D2; 16 | $secondary : #26A69A; 17 | $accent : #9C27B0; 18 | 19 | $dark : #1D1D1D; 20 | $dark-page : #121212; 21 | 22 | $positive : #21BA45; 23 | $negative : #C10015; 24 | $info : #31CCEC; 25 | $warning : #F2C037; 26 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= productName %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/utils/vn_chart.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author IITII 3 | * @date 2022/08/11 4 | */ 5 | 'use strict' 6 | const sample = { 7 | type: "line", 8 | data: { 9 | labels: [], 10 | datasets: [ 11 | { 12 | type: "", 13 | label: "", 14 | data: [], 15 | fill: false, 16 | backgroundColor: "", 17 | borderColor: "", 18 | lineTension: 0.1 19 | } 20 | ] 21 | }, 22 | options: { 23 | responsive: true, 24 | title: { 25 | display: true, 26 | text: "" 27 | }, 28 | tooltips: { 29 | mode: "index", 30 | intersect: false 31 | }, 32 | hover: { 33 | mode: "nearest", 34 | intersect: true 35 | }, 36 | scales: { 37 | xAxes: [ 38 | { 39 | display: true, 40 | scaleLabel: { 41 | display: true, 42 | labelString: "" 43 | } 44 | } 45 | ], 46 | yAxes: [ 47 | { 48 | display: true, 49 | scaleLabel: { 50 | display: true, 51 | labelString: "" 52 | } 53 | } 54 | ] 55 | } 56 | } 57 | } 58 | 59 | 60 | export function hoursChart(data, chartTitle, xTitle, yTitle,) { 61 | 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VnStatDashboard 2 | 3 | > A DashBoard for vnstat which using `chart.js` and `quasar` 4 | > [简体中文](https://iitii.github.io/2019/10/11/1/) 5 | #### Notice 6 | * WebServer such as Nginx must add CORS support: `add_header Access-Control-Allow-Origin *;` 7 | * It will auto-load `vnstat.json` and generate chart if you put a valid `vnstat.json` at the sibling directory 8 | * You can do like this if you want to build a traffic monitor site for you server 9 | 10 | ```bash 11 | sudo apt install nginx vnstat -y 12 | git clone https://github.com/IITII/VNSTAT-DASHBOARD /var/www/vnstat 13 | cd /var/www/vnstat && git checkout main 14 | #sed -i "s/root \S\+/root \/var\/www\/vnstat;/g" /etc/nginx/sites-available/default \ 15 | #&& nginx -t && nginx -s reload \ 16 | echo "59 * * * * /usr/bin/vnstat --json > /var/www/vnstat/vnstat.json" \ 17 | >> /var/spool/cron/crontabs/`whoami` 18 | crontab -l 19 | # Now open your browser, type server ip and see the DashBoard 20 | ``` 21 | #### ScreenShot 22 | 23 |  24 | 25 | #### How to Use ? 26 | > For normal use, you must make sure that vnstat had collected enough data. 27 | 28 | * Paste & Click Button. 29 | 30 | > Fixed! -> ~~For some reason, the interface id which you want to see must be "ens4"!!!~~ 31 | 32 | #### Know issues 33 | 34 | * Fixed! -> ~~Date maybe not correct in Hour Chart, especially in February & March.~~ 35 | -------------------------------------------------------------------------------- /.github/workflows/gh-page.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy Github Pages 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 🛎️ 11 | # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly. 12 | uses: actions/checkout@v3 13 | with: 14 | persist-credentials: false 15 | - name: Setup Node.js & Cache Dependencies ⚡️ 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: 14 19 | 20 | - name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built. 21 | run: | 22 | export PIC_DEPLOY_DIR='/VNSTAT-DASHBOARD' 23 | npm install 24 | npm run build 25 | 26 | - name: Deploy 🚀 27 | uses: JamesIves/github-pages-deploy-action@3.6.2 28 | with: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | BRANCH: gh-pages # The branch the action should deploy to. 31 | FOLDER: dist/spa # The folder the action should deploy. 32 | CLEAN: true # Automatically remove deleted files from the deploy branch 33 | -------------------------------------------------------------------------------- /src/utils/vn_utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author IITII 3 | * @date 2022/08/11 4 | */ 5 | 'use strict' 6 | import dayjs from 'dayjs' 7 | import objectSupport from 'dayjs/plugin/objectSupport' 8 | 9 | dayjs.extend(objectSupport) 10 | 11 | export function getDateTime(dateTime, format = 'YYYY/MM/DD HH:mm') { 12 | let { date, time } = dateTime 13 | if (date.month) { 14 | date.month -= 1 15 | } 16 | return dayjs({ ...date, ...time }).format(format) 17 | } 18 | // export function getDateTime(dateTime, format = 'YYYY/MM/DD HH:mm') { 19 | // const { date, time } = dateTime 20 | // let res = '' 21 | // res += getDate(date) 22 | // const d = getDate(time, ':') 23 | // res += d.length > 0 ? ` ${d}` : d 24 | // return dayjs(res, 'YYYY/MM/DD HH:mm').format(format) 25 | // } 26 | 27 | // function getDate(date, sep = '/') { 28 | // if (!date) return '' 29 | // let dateS = '' 30 | // const d = new Date() 31 | // d.setFullYear(date.year, date.month - 1, date.day) 32 | // d.setHours(date.hour, date.minute, date.second) 33 | 34 | // const format = { year: 4, month: 2, day: 2, hour: 2, minute: 2, second: 2 } 35 | // Object.keys(date).forEach(k => { 36 | // const v = `${date[k]}`.padStart(format[k], '0') 37 | // dateS += dateS.length > 0 ? `${sep}${v}` : `${v}` 38 | // }) 39 | // return dateS 40 | // } 41 | 42 | export function getTraffic(s, showUnit = true, gb = 1024 * 1024 * 1024) { 43 | let res = '' 44 | if (!s) { 45 | res += '0' 46 | } else { 47 | res += (s / gb).toFixed(2) 48 | } 49 | res = parseFloat(res) 50 | if (showUnit) { 51 | res += 'GB' 52 | } 53 | return res 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vnstat_dashboard", 3 | "version": "0.0.1", 4 | "description": "VnStat Dashboard", 5 | "productName": "VnStatDashboard", 6 | "author": "IITII ", 7 | "private": true, 8 | "scripts": { 9 | "local": "quasar dev", 10 | "dev": "export NODE_OPTIONS=--openssl-legacy-provider && quasar dev", 11 | "build": "quasar build", 12 | "lint": "eslint --ext .js,.vue ./", 13 | "test": "echo \"No test specified\" && exit 0" 14 | }, 15 | "dependencies": { 16 | "@kurkle/color": "^0.2.1", 17 | "@quasar/extras": "^1.0.0", 18 | "axios": "^0.21.1", 19 | "chart.js": "^3.9.1", 20 | "chartjs-adapter-luxon": "^1.2.0", 21 | "core-js": "^3.6.5", 22 | "dayjs": "^1.11.4", 23 | "luxon": "^3.0.1", 24 | "quasar": "^1.0.0", 25 | "vue-chartjs": "^4.1.1", 26 | "vue-i18n": "^8.0.0" 27 | }, 28 | "devDependencies": { 29 | "@babel/eslint-parser": "^7.13.14", 30 | "eslint": "^8.10.0", 31 | "eslint-plugin-vue": "^9.0.0", 32 | "eslint-webpack-plugin": "^2.0.0", 33 | "eslint-config-prettier": "^8.1.0", 34 | "prettier": "^2.5.1", 35 | "@quasar/app": "^2.0.0", 36 | "vue": "^2.7.8", 37 | "vue-template-compiler": "^2.7.8", 38 | "vuejs-logger": "^1.5.5" 39 | }, 40 | "browserslist": [ 41 | "last 10 Chrome versions", 42 | "last 10 Firefox versions", 43 | "last 4 Edge versions", 44 | "last 7 Safari versions", 45 | "last 8 Android versions", 46 | "last 8 ChromeAndroid versions", 47 | "last 8 FirefoxAndroid versions", 48 | "last 10 iOS versions", 49 | "last 5 Opera versions" 50 | ], 51 | "engines": { 52 | "node": ">= 10.18.1", 53 | "npm": ">= 6.13.4", 54 | "yarn": ">= 1.21.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy 4 | // This option interrupts the configuration hierarchy at this file 5 | // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) 6 | root: true, 7 | 8 | parserOptions: { 9 | parser: '@babel/eslint-parser', 10 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 11 | sourceType: 'module' // Allows for the use of imports 12 | }, 13 | 14 | env: { 15 | browser: true, 16 | 'vue/setup-compiler-macros': true 17 | }, 18 | 19 | // Rules order is important, please avoid shuffling them 20 | extends: [ 21 | // Base ESLint recommended rules 22 | // 'eslint:recommended', 23 | 24 | // Uncomment any of the lines below to choose desired strictness, 25 | // but leave only one uncommented! 26 | // See https://eslint.vuejs.org/rules/#available-rules (look for Vuejs 2 ones) 27 | 'plugin:vue/essential', // Priority A: Essential (Error Prevention) 28 | // 'plugin:vue/strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) 29 | // 'plugin:vue/recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) 30 | 31 | // https://github.com/prettier/eslint-config-prettier#installation 32 | // usage with Prettier, provided by 'eslint-config-prettier'. 33 | 'prettier' 34 | ], 35 | 36 | plugins: [ 37 | // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-file 38 | // required to lint *.vue files 39 | 'vue', 40 | 41 | // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674 42 | // Prettier has not been included as plugin to avoid performance impact 43 | // add it as an extension for your IDE 44 | 45 | ], 46 | 47 | globals: { 48 | ga: 'readonly', // Google Analytics 49 | cordova: 'readonly', 50 | __statics: 'readonly', 51 | process: 'readonly', 52 | Capacitor: 'readonly', 53 | chrome: 'readonly' 54 | }, 55 | 56 | // add your custom rules here 57 | rules: { 58 | 59 | 'prefer-promise-reject-errors': 'off', 60 | 61 | 'vue/multi-word-component-names': 'off', 62 | 63 | // allow debugger during development only 64 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build Release 2 | on: 3 | push: 4 | tags: 5 | - 'v**' 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | include: 12 | - os: ubuntu-latest 13 | artifact_name: zip/zip.zip 14 | asset_name: zip.zip 15 | release_name: zip 16 | deploy_dir: '/' 17 | - os: ubuntu-latest 18 | artifact_name: zip_vn/zip_vn.zip 19 | asset_name: zip_vn.zip 20 | release_name: zip_vn 21 | deploy_dir: '/vn' 22 | steps: 23 | - name: Checkout 🛎️ 24 | # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly. 25 | uses: actions/checkout@v3 26 | with: 27 | persist-credentials: false 28 | - name: Setup Node.js & Cache Dependencies ⚡️ 29 | uses: actions/setup-node@v3 30 | with: 31 | node-version: 14 32 | 33 | #See:// https://github.com/actions/virtual-environments/blob/ubuntu18/20201004.1/images/linux/Ubuntu1804-README.md 34 | # - name: Install build dependencies 35 | # run: | 36 | # sudo apt-get update 37 | # sudo apt-get install -y zip git 38 | 39 | - name: Set Environment 🔨 40 | run: | 41 | sudo npm install -g @quasar/cli 42 | - name: Install and Build 🔧 43 | run: | 44 | npm install 45 | npm run build 46 | - name: Package Artifact 🎁 47 | run: | 48 | export RELEASE_NAME=${{ matrix.release_name }} 49 | export PIC_DEPLOY_DIR=${{ matrix.deploy_dir }} 50 | mkdir ${RELEASE_NAME} 51 | zip -r ${RELEASE_NAME}/${RELEASE_NAME}.zip dist/spa/* 52 | ls -alh ${RELEASE_NAME} 53 | # - name: Get package name 54 | # id: get_package 55 | # run: | 56 | # export RELEASE_NAME="release" 57 | # echo ::set-output name=NAME::$(ls . | grep ${RELEASE_NAME}) 58 | 59 | - name: Upload Artifact 🚀 60 | uses: actions/upload-artifact@v2 61 | with: 62 | name: ${{ matrix.asset_name }} 63 | path: ${{ matrix.artifact_name }} 64 | 65 | - name: Upload binaries to release ☕ 66 | uses: svenstaro/upload-release-action@v1-release 67 | with: 68 | repo_token: ${{ secrets.GITHUB_TOKEN }} 69 | file: ${{ matrix.artifact_name }} 70 | asset_name: ${{ matrix.asset_name }} 71 | tag: ${{ github.ref }} 72 | overwrite: true 73 | -------------------------------------------------------------------------------- /src/components/VnChart.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 111 | 112 | 115 | -------------------------------------------------------------------------------- /src/assets/quasar-logo-vertical.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /src/utils/chart_utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author IITII 3 | * @date 2022/08/11 4 | */ 5 | 'use strict' 6 | import colorLib from '@kurkle/color' 7 | // import { DateTime } from 'luxon' 8 | import 'chartjs-adapter-luxon' 9 | // import { valueOrDefault } from '../../dist/helpers.js' 10 | 11 | // Adapted from http://indiegamr.com/generate-repeatable-random-numbers-in-js/ 12 | let _seed = Date.now() 13 | 14 | export function srand(seed) { 15 | _seed = seed 16 | } 17 | 18 | // export function rand(min, max) { 19 | // min = valueOrDefault(min, 0) 20 | // max = valueOrDefault(max, 0) 21 | // _seed = (_seed * 9301 + 49297) % 233280 22 | // return min + (_seed / 233280) * (max - min) 23 | // } 24 | // 25 | // export function numbers(config) { 26 | // let cfg = config || {} 27 | // let min = valueOrDefault(cfg.min, 0) 28 | // let max = valueOrDefault(cfg.max, 100) 29 | // let from = valueOrDefault(cfg.from, []) 30 | // let count = valueOrDefault(cfg.count, 8) 31 | // let decimals = valueOrDefault(cfg.decimals, 8) 32 | // let continuity = valueOrDefault(cfg.continuity, 1) 33 | // let dfactor = Math.pow(10, decimals) || 0 34 | // let data = [] 35 | // let i, value 36 | // 37 | // for (i = 0; i < count; ++i) { 38 | // value = (from[i] || 0) + this.rand(min, max) 39 | // if (this.rand() <= continuity) { 40 | // data.push(Math.round(dfactor * value) / dfactor) 41 | // } else { 42 | // data.push(null) 43 | // } 44 | // } 45 | // 46 | // return data 47 | // } 48 | 49 | export function points(config) { 50 | const xs = this.numbers(config) 51 | const ys = this.numbers(config) 52 | return xs.map((x, i) => ({ x, y: ys[i] })) 53 | } 54 | 55 | export function bubbles(config) { 56 | return this.points(config).map(pt => { 57 | pt.r = this.rand(config.rmin, config.rmax) 58 | return pt 59 | }) 60 | } 61 | 62 | export function labels(config) { 63 | let cfg = config || {} 64 | let min = cfg.min || 0 65 | let max = cfg.max || 100 66 | let count = cfg.count || 8 67 | let step = (max - min) / count 68 | let decimals = cfg.decimals || 8 69 | let dfactor = Math.pow(10, decimals) || 0 70 | let prefix = cfg.prefix || '' 71 | let values = [] 72 | let i 73 | 74 | for (i = min; i < max; i += step) { 75 | values.push(prefix + Math.round(dfactor * i) / dfactor) 76 | } 77 | 78 | return values 79 | } 80 | 81 | const MONTHS = [ 82 | 'January', 83 | 'February', 84 | 'March', 85 | 'April', 86 | 'May', 87 | 'June', 88 | 'July', 89 | 'August', 90 | 'September', 91 | 'October', 92 | 'November', 93 | 'December' 94 | ] 95 | 96 | export function months(config) { 97 | let cfg = config || {} 98 | let count = cfg.count || 12 99 | let section = cfg.section 100 | let values = [] 101 | let i, value 102 | 103 | for (i = 0; i < count; ++i) { 104 | value = MONTHS[Math.ceil(i) % 12] 105 | values.push(value.substring(0, section)) 106 | } 107 | 108 | return values 109 | } 110 | 111 | const COLORS = [ 112 | '#4dc9f6', 113 | '#f67019', 114 | '#f53794', 115 | '#537bc4', 116 | '#acc236', 117 | '#166a8f', 118 | '#00a950', 119 | '#58595b', 120 | '#8549ba' 121 | ] 122 | 123 | export function color(index) { 124 | return COLORS[index % COLORS.length] 125 | } 126 | 127 | export function transparentize(value, opacity) { 128 | let alpha = opacity === undefined ? 0.5 : 1 - opacity 129 | return colorLib(value).alpha(alpha).rgbString() 130 | } 131 | 132 | export const CHART_COLORS = { 133 | red: 'rgb(255, 99, 132)', 134 | orange: 'rgb(255, 159, 64)', 135 | yellow: 'rgb(255, 205, 86)', 136 | green: 'rgb(75, 192, 192)', 137 | blue: 'rgb(54, 162, 235)', 138 | blue_1:"rgb(5, 162, 235)", 139 | blue_5:"rgb(25, 99, 132)", 140 | purple: 'rgb(153, 102, 255)', 141 | grey: 'rgb(201, 203, 207)' 142 | } 143 | 144 | const NAMED_COLORS = [ 145 | CHART_COLORS.red, 146 | CHART_COLORS.orange, 147 | CHART_COLORS.yellow, 148 | CHART_COLORS.green, 149 | CHART_COLORS.blue, 150 | CHART_COLORS.purple, 151 | CHART_COLORS.grey, 152 | ] 153 | 154 | export function namedColor(index) { 155 | return NAMED_COLORS[index % NAMED_COLORS.length] 156 | } 157 | 158 | // export function newDate(days) { 159 | // return DateTime.now().plus({ days }).toJSDate() 160 | // } 161 | // 162 | // export function newDateString(days) { 163 | // return DateTime.now().plus({ days }).toISO() 164 | // } 165 | // 166 | // export function parseISODate(str) { 167 | // return DateTime.fromISO(str) 168 | // } 169 | -------------------------------------------------------------------------------- /src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 提交 8 | 11 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 180 | -------------------------------------------------------------------------------- /src/pages/VnDashboard.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 183 | 184 | 186 | -------------------------------------------------------------------------------- /quasar.conf.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | /* 4 | * This file runs in a Node context (it's NOT transpiled by Babel), so use only 5 | * the ES6 features that are supported by your Node version. https://node.green/ 6 | */ 7 | 8 | // Configuration for your app 9 | // https://v1.quasar.dev/quasar-cli/quasar-conf-js 10 | 11 | const ESLintPlugin = require('eslint-webpack-plugin') 12 | 13 | module.exports = function (/* ctx */) { 14 | return { 15 | // https://v1.quasar.dev/quasar-cli/supporting-ts 16 | supportTS: false, 17 | 18 | // https://v1.quasar.dev/quasar-cli/prefetch-feature 19 | // preFetch: true, 20 | 21 | // app boot file (/src/boot) 22 | // --> boot files are part of "main.js" 23 | // https://v1.quasar.dev/quasar-cli/boot-files 24 | boot: [ 25 | 'logger', 26 | 'i18n', 27 | 'axios', 28 | ], 29 | 30 | // https://v1.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css 31 | css: [ 32 | 'app.scss' 33 | ], 34 | 35 | // https://github.com/quasarframework/quasar/tree/dev/extras 36 | extras: [ 37 | // 'ionicons-v4', 38 | // 'mdi-v5', 39 | // 'fontawesome-v6', 40 | // 'eva-icons', 41 | // 'themify', 42 | // 'line-awesome', 43 | // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! 44 | 45 | 'roboto-font', // optional, you are not bound to it 46 | 'material-icons', // optional, you are not bound to it 47 | ], 48 | 49 | // Full list of options: https://v1.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build 50 | build: { 51 | vueRouterMode: 'hash', // available values: 'hash', 'history' 52 | 53 | showProgress: true, 54 | gzip: true, 55 | sourceMap: process.env.NODE_ENV !== 'production', 56 | minify: process.env.NODE_ENV === 'production', 57 | publicPath: process.env.PIC_DEPLOY_DIR || '/', 58 | distDir: process.env.PIC_OUTPUT_DIR || undefined, 59 | 60 | // transpile: false, 61 | 62 | // Add dependencies for transpiling with Babel (Array of string/regex) 63 | // (from node_modules, which are by default not transpiled). 64 | // Applies only if "transpile" is set to true. 65 | // transpileDependencies: [], 66 | 67 | // rtl: false, // https://v1.quasar.dev/options/rtl-support 68 | // preloadChunks: true, 69 | // showProgress: false, 70 | // gzip: true, 71 | // analyze: true, 72 | 73 | // Options below are automatically set depending on the env, set them if you want to override 74 | // extractCSS: false, 75 | 76 | // https://v1.quasar.dev/quasar-cli/handling-webpack 77 | // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain 78 | chainWebpack(chain) { 79 | chain.plugin('eslint-webpack-plugin') 80 | .use(ESLintPlugin, [{ extensions: ['js', 'vue'] }]) 81 | } 82 | }, 83 | 84 | // Full list of options: https://v1.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-devServer 85 | devServer: { 86 | https: false, 87 | port: 8080, 88 | open: true // opens browser window automatically 89 | }, 90 | 91 | // https://v1.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework 92 | framework: { 93 | iconSet: 'material-icons', // Quasar icon set 94 | lang: 'zh-hans', // Quasar language pack 95 | config: { 96 | // dark: 'auto', 97 | notify: { 98 | html: false, 99 | position: 'top-right', 100 | timeout: 2500, 101 | // closeBtn: true 102 | closeBtn: '×' 103 | } 104 | }, 105 | 106 | // Possible values for "importStrategy": 107 | // * 'auto' - (DEFAULT) Auto-import needed Quasar components & directives 108 | // * 'all' - Manually specify what to import 109 | importStrategy: 'auto', 110 | 111 | // For special cases outside of where "auto" importStrategy can have an impact 112 | // (like functional components as one of the examples), 113 | // you can manually specify Quasar components/directives to be available everywhere: 114 | // 115 | // components: [], 116 | // directives: [], 117 | 118 | // Quasar plugins 119 | plugins: [ 120 | 'LocalStorage', 121 | // 'SessionStorage', 122 | 'Notify', 123 | 'Dialog', 124 | 'Loading', 125 | ], 126 | }, 127 | 128 | // animations: 'all', // --- includes all animations 129 | // https://v1.quasar.dev/options/animations 130 | animations: [], 131 | 132 | // https://v1.quasar.dev/quasar-cli/developing-ssr/configuring-ssr 133 | ssr: { 134 | pwa: false 135 | }, 136 | 137 | // https://v1.quasar.dev/quasar-cli/developing-pwa/configuring-pwa 138 | pwa: { 139 | workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest' 140 | workboxOptions: {}, // only for GenerateSW 141 | manifest: { 142 | name: `VnStatDashboard`, 143 | short_name: `VnStatDashboard`, 144 | description: `VnStat Dashboard`, 145 | display: 'standalone', 146 | orientation: 'portrait', 147 | background_color: '#ffffff', 148 | theme_color: '#027be3', 149 | icons: [ 150 | { 151 | src: 'icons/icon-128x128.png', 152 | sizes: '128x128', 153 | type: 'image/png' 154 | }, 155 | { 156 | src: 'icons/icon-192x192.png', 157 | sizes: '192x192', 158 | type: 'image/png' 159 | }, 160 | { 161 | src: 'icons/icon-256x256.png', 162 | sizes: '256x256', 163 | type: 'image/png' 164 | }, 165 | { 166 | src: 'icons/icon-384x384.png', 167 | sizes: '384x384', 168 | type: 'image/png' 169 | }, 170 | { 171 | src: 'icons/icon-512x512.png', 172 | sizes: '512x512', 173 | type: 'image/png' 174 | } 175 | ] 176 | } 177 | }, 178 | 179 | // Full list of options: https://v1.quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova 180 | cordova: { 181 | // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing 182 | }, 183 | 184 | // Full list of options: https://v1.quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor 185 | capacitor: { 186 | hideSplashscreen: true 187 | }, 188 | 189 | // Full list of options: https://v1.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron 190 | electron: { 191 | bundler: 'packager', // 'packager' or 'builder' 192 | 193 | packager: { 194 | // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options 195 | 196 | // OS X / Mac App Store 197 | // appBundleId: '', 198 | // appCategoryType: '', 199 | // osxSign: '', 200 | // protocol: 'myapp://path', 201 | 202 | // Windows only 203 | // win32metadata: { ... } 204 | }, 205 | 206 | builder: { 207 | // https://www.electron.build/configuration/configuration 208 | 209 | appId: 'vnstat_dashboard' 210 | }, 211 | 212 | // More info: https://v1.quasar.dev/quasar-cli/developing-electron-apps/node-integration 213 | nodeIntegration: true, 214 | 215 | extendWebpack(/* cfg */) { 216 | // do something with Electron main process Webpack cfg 217 | // chainWebpack also available besides this extendWebpack 218 | } 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /docs/demo.json: -------------------------------------------------------------------------------- 1 | {"vnstatversion":"2.9","jsonversion":"2","interfaces":[{"name":"ens3","alias":"","created":{"date":{"year":2022,"month":3,"day":19}},"updated":{"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":55}},"traffic":{"total":{"rx":319916132137,"tx":352800998163},"fiveminute":[{"id":2,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":0},"rx":578545,"tx":1965096},{"id":1,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":5},"rx":383561,"tx":1205087},{"id":3,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":10},"rx":182842,"tx":1079831},{"id":4,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":15},"rx":946338,"tx":1974650},{"id":5,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":20},"rx":23979607,"tx":10686333},{"id":6,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":25},"rx":18248947,"tx":23588496},{"id":7,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":30},"rx":21657445,"tx":25915830},{"id":8,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":35},"rx":8494733,"tx":18128343},{"id":9,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":40},"rx":940736,"tx":8446688},{"id":10,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":45},"rx":199629,"tx":1094911},{"id":11,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":50},"rx":1327690,"tx":2372226}],"hour":[{"id":10,"date":{"year":2022,"month":8,"day":11},"time":{"hour":14,"minute":0},"rx":67863552,"tx":81939456},{"id":9,"date":{"year":2022,"month":8,"day":11},"time":{"hour":15,"minute":0},"rx":980981760,"tx":863624192},{"id":8,"date":{"year":2022,"month":8,"day":11},"time":{"hour":16,"minute":0},"rx":969036800,"tx":1045515264},{"id":7,"date":{"year":2022,"month":8,"day":11},"time":{"hour":17,"minute":0},"rx":735665152,"tx":749642752},{"id":6,"date":{"year":2022,"month":8,"day":11},"time":{"hour":18,"minute":0},"rx":13315072,"tx":26512384},{"id":5,"date":{"year":2022,"month":8,"day":11},"time":{"hour":19,"minute":0},"rx":4962304,"tx":16333824},{"id":4,"date":{"year":2022,"month":8,"day":11},"time":{"hour":20,"minute":0},"rx":3954688,"tx":15693824},{"id":3,"date":{"year":2022,"month":8,"day":11},"time":{"hour":21,"minute":0},"rx":6162432,"tx":17848320},{"id":2,"date":{"year":2022,"month":8,"day":11},"time":{"hour":22,"minute":0},"rx":10393600,"tx":22779904},{"id":1,"date":{"year":2022,"month":8,"day":11},"time":{"hour":23,"minute":0},"rx":42924032,"tx":38481920},{"id":23,"date":{"year":2022,"month":8,"day":12},"time":{"hour":0,"minute":0},"rx":155386880,"tx":168017920},{"id":22,"date":{"year":2022,"month":8,"day":12},"time":{"hour":1,"minute":0},"rx":16556032,"tx":28097536},{"id":21,"date":{"year":2022,"month":8,"day":12},"time":{"hour":2,"minute":0},"rx":14823424,"tx":26513408},{"id":20,"date":{"year":2022,"month":8,"day":12},"time":{"hour":3,"minute":0},"rx":5379072,"tx":16614400},{"id":19,"date":{"year":2022,"month":8,"day":12},"time":{"hour":4,"minute":0},"rx":3072000,"tx":14251008},{"id":18,"date":{"year":2022,"month":8,"day":12},"time":{"hour":5,"minute":0},"rx":3633152,"tx":14885888},{"id":17,"date":{"year":2022,"month":8,"day":12},"time":{"hour":6,"minute":0},"rx":2630656,"tx":13704192},{"id":16,"date":{"year":2022,"month":8,"day":12},"time":{"hour":7,"minute":0},"rx":71891968,"tx":86114304},{"id":15,"date":{"year":2022,"month":8,"day":12},"time":{"hour":8,"minute":0},"rx":637999104,"tx":650112000},{"id":14,"date":{"year":2022,"month":8,"day":12},"time":{"hour":9,"minute":0},"rx":980818944,"tx":1000552448},{"id":13,"date":{"year":2022,"month":8,"day":12},"time":{"hour":10,"minute":0},"rx":738167808,"tx":754173952},{"id":12,"date":{"year":2022,"month":8,"day":12},"time":{"hour":11,"minute":0},"rx":575649792,"tx":531624960},{"id":11,"date":{"year":2022,"month":8,"day":12},"time":{"hour":12,"minute":0},"rx":6773760,"tx":17872896},{"id":24,"date":{"year":2022,"month":8,"day":12},"time":{"hour":13,"minute":0},"rx":76940073,"tx":96457491}],"day":[{"id":1,"date":{"year":2022,"month":7,"day":14},"rx":5663744000,"tx":6164121600},{"id":2,"date":{"year":2022,"month":7,"day":15},"rx":3609410560,"tx":3893444608},{"id":3,"date":{"year":2022,"month":7,"day":16},"rx":2787852288,"tx":2906098688},{"id":4,"date":{"year":2022,"month":7,"day":17},"rx":5257546752,"tx":5579061248},{"id":5,"date":{"year":2022,"month":7,"day":18},"rx":7194637312,"tx":7525683200},{"id":6,"date":{"year":2022,"month":7,"day":19},"rx":3367032832,"tx":3666861056},{"id":7,"date":{"year":2022,"month":7,"day":20},"rx":2349650944,"tx":2714771456},{"id":8,"date":{"year":2022,"month":7,"day":21},"rx":6467853312,"tx":6865112064},{"id":9,"date":{"year":2022,"month":7,"day":22},"rx":2017329152,"tx":2284185600},{"id":10,"date":{"year":2022,"month":7,"day":23},"rx":159774720,"tx":403557376},{"id":11,"date":{"year":2022,"month":7,"day":24},"rx":1092962304,"tx":1452672000},{"id":12,"date":{"year":2022,"month":7,"day":25},"rx":903505920,"tx":1150124032},{"id":13,"date":{"year":2022,"month":7,"day":26},"rx":2093518848,"tx":2304783360},{"id":14,"date":{"year":2022,"month":7,"day":27},"rx":2574479360,"tx":2769058816},{"id":15,"date":{"year":2022,"month":7,"day":28},"rx":4631707648,"tx":5134836736},{"id":16,"date":{"year":2022,"month":7,"day":29},"rx":14984620032,"tx":15535862784},{"id":17,"date":{"year":2022,"month":7,"day":30},"rx":1367498752,"tx":1684364288},{"id":18,"date":{"year":2022,"month":7,"day":31},"rx":2937230336,"tx":3367526400},{"id":19,"date":{"year":2022,"month":8,"day":1},"rx":61894045696,"tx":65980303360},{"id":20,"date":{"year":2022,"month":8,"day":2},"rx":9056942080,"tx":10173101056},{"id":21,"date":{"year":2022,"month":8,"day":3},"rx":17383775232,"tx":18959910912},{"id":22,"date":{"year":2022,"month":8,"day":4},"rx":4461379584,"tx":5020328960},{"id":23,"date":{"year":2022,"month":8,"day":5},"rx":11197784064,"tx":12169101312},{"id":24,"date":{"year":2022,"month":8,"day":6},"rx":1705209856,"tx":2042448896},{"id":25,"date":{"year":2022,"month":8,"day":7},"rx":2067992576,"tx":2466149376},{"id":26,"date":{"year":2022,"month":8,"day":8},"rx":2579404800,"tx":3087752192},{"id":27,"date":{"year":2022,"month":8,"day":9},"rx":5789436928,"tx":5821197312},{"id":28,"date":{"year":2022,"month":8,"day":10},"rx":3828857856,"tx":4230946816},{"id":29,"date":{"year":2022,"month":8,"day":11},"rx":5277888512,"tx":5448190976},{"id":30,"date":{"year":2022,"month":8,"day":12},"rx":3289722665,"tx":3418992403}],"month":[{"id":1,"date":{"year":2022,"month":3},"rx":14473970688,"tx":14601482240},{"id":2,"date":{"year":2022,"month":4},"rx":22626552832,"tx":24645004288},{"id":3,"date":{"year":2022,"month":5},"rx":9102994432,"tx":11243105280},{"id":4,"date":{"year":2022,"month":6},"rx":43554382848,"tx":51779832832},{"id":5,"date":{"year":2022,"month":7},"rx":101625791488,"tx":111713149952},{"id":6,"date":{"year":2022,"month":8},"rx":128532439849,"tx":138818423571}],"year":[{"id":1,"date":{"year":2022},"rx":319916132137,"tx":352800998163}],"top":[{"id":10,"date":{"year":2022,"month":8,"day":1},"rx":61894045696,"tx":65980303360},{"id":9,"date":{"year":2022,"month":8,"day":3},"rx":17383775232,"tx":18959910912},{"id":8,"date":{"year":2022,"month":7,"day":29},"rx":14984620032,"tx":15535862784},{"id":7,"date":{"year":2022,"month":8,"day":5},"rx":11197784064,"tx":12169101312},{"id":6,"date":{"year":2022,"month":7,"day":10},"rx":9400890368,"tx":9831262208},{"id":5,"date":{"year":2022,"month":8,"day":2},"rx":9056942080,"tx":10173101056},{"id":4,"date":{"year":2022,"month":7,"day":8},"rx":8210963456,"tx":8620388352},{"id":3,"date":{"year":2022,"month":7,"day":18},"rx":7194637312,"tx":7525683200},{"id":2,"date":{"year":2022,"month":7,"day":21},"rx":6467853312,"tx":6865112064},{"id":1,"date":{"year":2022,"month":7,"day":14},"rx":5663744000,"tx":6164121600},{"id":11,"date":{"year":2022,"month":8,"day":12},"rx":76940073,"tx":96457491}]}}]} 2 | --------------------------------------------------------------------------------