diff --git a/.env b/.env index 2aa6918..a4932a8 100644 --- a/.env +++ b/.env @@ -1,3 +1,10 @@ +# Database Configuration +VITE_DB_HOST=db.pandem.fr +VITE_DB_USER=proxdash +VITE_DB_PASSWORD=proxdash +VITE_DB_NAME=proxdash -VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZwZmJzY2psanBtcGJjb2hteHJtIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDIxNTk1NjIsImV4cCI6MjA1NzczNTU2Mn0.bMHEDr8XS2fFVr38JuCJOF80sWU3P4isQ1DStRuwUiw -VITE_SUPABASE_URL=https://vpfbscjljpmpbcohmxrm.supabase.co \ No newline at end of file +# InfluxDB Configuration (if needed) +VITE_INFLUX_URL= +VITE_INFLUX_TOKEN= +VITE_INFLUX_ORG= \ No newline at end of file diff --git a/index.html b/index.html index e0eb7a4..e4b78ea 100644 --- a/index.html +++ b/index.html @@ -2,12 +2,12 @@ - + - Proxmox Chooser + Vite + React + TS
- \ No newline at end of file + diff --git a/package-lock.json b/package-lock.json index 62446ab..c709eb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { - "name": "vite-react-typescript-starter", + "name": "proxmox-dashboard", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "vite-react-typescript-starter", + "name": "proxmox-dashboard", "version": "0.0.0", "dependencies": { "@supabase/supabase-js": "^2.39.7", - "framer-motion": "^11.0.8", "lucide-react": "^0.344.0", + "mysql2": "^3.9.2", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "recharts": "^2.12.2" }, "devDependencies": { "@eslint/js": "^9.9.1", @@ -292,6 +293,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", @@ -1313,6 +1325,60 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -1749,6 +1815,14 @@ "postcss": "^8.1.0" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1909,6 +1983,14 @@ "node": ">= 6" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1974,8 +2056,117 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } }, "node_modules/debug": { "version": "4.3.7", @@ -1994,12 +2185,25 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2012,6 +2216,15 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2336,12 +2549,25 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-equals": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -2479,32 +2705,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/framer-motion": { - "version": "11.18.2", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", - "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", - "dependencies": { - "motion-dom": "^11.18.1", - "motion-utils": "^11.18.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2528,6 +2728,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2632,6 +2840,17 @@ "node": ">= 0.4" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2666,6 +2885,14 @@ "node": ">=0.8.19" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2732,6 +2959,11 @@ "node": ">=0.12.0" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2873,12 +3105,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2899,6 +3141,20 @@ "yallist": "^3.0.2" } }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/lucide-react": { "version": "0.344.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.344.0.tgz", @@ -2950,25 +3206,31 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/motion-dom": { - "version": "11.18.1", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", - "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", - "dependencies": { - "motion-utils": "^11.18.1" - } - }, - "node_modules/motion-utils": { - "version": "11.18.1", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", - "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/mysql2": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.13.0.tgz", + "integrity": "sha512-M6DIQjTqKeqXH5HLbLMxwcK5XfXHw30u5ap6EZmu7QVmcF/gnh2wS/EOiQ4MTbXz/vQeoXrmycPlVRM00WSslg==", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -2980,6 +3242,25 @@ "thenify-all": "^1.0.0" } }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -3032,7 +3313,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3357,6 +3637,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3409,6 +3704,11 @@ "react": "^18.3.1" } }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -3418,6 +3718,35 @@ "node": ">=0.10.0" } }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -3439,6 +3768,41 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz", + "integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -3533,6 +3897,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -3550,6 +3919,11 @@ "semver": "bin/semver.js" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3592,6 +3966,14 @@ "node": ">=0.10.0" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -3810,6 +4192,11 @@ "node": ">=0.8" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -3854,11 +4241,6 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3957,6 +4339,27 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/vite": { "version": "5.4.8", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", diff --git a/package.json b/package.json index 95968d2..9242d51 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "vite-react-typescript-starter", + "name": "proxmox-dashboard", "private": true, "version": "0.0.0", "type": "module", @@ -12,9 +12,10 @@ "dependencies": { "@supabase/supabase-js": "^2.39.7", "lucide-react": "^0.344.0", + "mysql2": "^3.9.2", "react": "^18.3.1", "react-dom": "^18.3.1", - "framer-motion": "^11.0.8" + "recharts": "^2.12.2" }, "devDependencies": { "@eslint/js": "^9.9.1", diff --git a/src/App.tsx b/src/App.tsx index d1ee512..b316c91 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,388 +1,79 @@ -import React, { useState, useEffect } from 'react'; -import { Plus, ExternalLink, Trash2, Activity } from 'lucide-react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { supabase } from './supabase'; - -// Custom Proxmox Logo SVG component -const ProxmoxLogo = ({ className }: { className?: string }) => ( - - - -); - -interface ServerSpecs { - cpu: string; - ram: string; - gpu: string; - type: string; -} - -interface ProxmoxServer { - id: string; - name: string; - url: string; - status: 'online' | 'offline' | 'checking'; - specs: ServerSpecs; - lastPing?: number; -} +import React, { useEffect, useState } from 'react'; +import { PlusCircle } from 'lucide-react'; +import { AddServerModal } from './components/AddServerModal'; +import { ServerCard } from './components/ServerCard'; +import { Server, ServerMetrics } from './types'; +import { supabase } from './lib/supabase'; function App() { - const [servers, setServers] = useState([]); - const [isModalOpen, setIsModalOpen] = useState(false); - const [newServer, setNewServer] = useState({ - name: '', - url: '', - specs: { - cpu: '', - ram: '', - gpu: '', - type: '' - } - }); + const [servers, setServers] = useState([]); + const [showAddModal, setShowAddModal] = useState(false); + const [metrics, setMetrics] = useState>({}); - // Load servers from Supabase useEffect(() => { - const loadServers = async () => { - const { data, error } = await supabase - .from('servers') - .select('*'); - - if (error) { - console.error('Error loading servers:', error); - return; - } - - setServers(data.map(server => ({ - ...server, - specs: server.specs, - lastPing: server.last_ping - }))); - }; - - loadServers(); + fetchServers(); }, []); - const pingServer = async (server: ProxmoxServer) => { - setServers(current => - current.map(s => - s.id === server.id ? { ...s, status: 'checking' } : s - ) - ); - - try { - const startTime = Date.now(); - const response = await fetch(server.url, { mode: 'no-cors' }); - const endTime = Date.now(); - const pingTime = endTime - startTime; - - // Update server status in Supabase - await supabase - .from('servers') - .update({ - status: 'online', - last_ping: pingTime - }) - .eq('id', server.id); - - setServers(current => - current.map(s => - s.id === server.id - ? { ...s, status: 'online', lastPing: pingTime } - : s - ) - ); - } catch (error) { - // Update server status in Supabase - await supabase - .from('servers') - .update({ - status: 'offline', - last_ping: null - }) - .eq('id', server.id); - - setServers(current => - current.map(s => - s.id === server.id ? { ...s, status: 'offline' } : s - ) - ); - } - }; - - const addServer = async () => { - if (newServer.name && newServer.url) { - const { data: serverData, error } = await supabase - .from('servers') - .insert([{ - name: newServer.name, - url: newServer.url, - specs: newServer.specs, - status: 'checking' - }]) - .select() - .single(); - - if (error) { - console.error('Error adding server:', error); - return; - } - - const server: ProxmoxServer = { - ...serverData, - specs: serverData.specs, - lastPing: serverData.last_ping - }; - - setServers([...servers, server]); - setNewServer({ - name: '', - url: '', - specs: { cpu: '', ram: '', gpu: '', type: '' } - }); - setIsModalOpen(false); - pingServer(server); - } - }; - - const deleteServer = async (id: string) => { - const { error } = await supabase + const fetchServers = async () => { + const { data } = await supabase .from('servers') - .delete() - .eq('id', id); - - if (error) { - console.error('Error deleting server:', error); - return; + .select('*') + .order('created_at', { ascending: true }); + + if (data) { + setServers(data); + data.forEach(server => { + fetchMetrics(server); + }); } - - setServers(servers.filter(server => server.id !== id)); }; - useEffect(() => { - const interval = setInterval(() => { - servers.forEach(server => pingServer(server)); - }, 30000); - - return () => clearInterval(interval); - }, [servers]); + const fetchMetrics = async (server: Server) => { + // In a real implementation, this would fetch from InfluxDB + // For demo purposes, we'll generate mock data + const mockMetrics: ServerMetrics[] = Array.from({ length: 20 }).map((_, i) => ({ + cpu_usage: Math.random() * 100, + ram_usage: Math.random() * 100, + timestamp: new Date(Date.now() - (19 - i) * 60000).toISOString() + })); + + setMetrics(prev => ({ + ...prev, + [server.id]: mockMetrics + })); + }; return ( -
- {/* Header */} -
-
-
-
- -

Proxmox Dashboard

-
- -
-
-
- - {/* Main Content */} -
-
- - {servers.map(server => ( - -
-
-
-
-

{server.name}

-

{server.url}

-
-
- - - -
-
-
- - {server.status} - - {server.lastPing && server.status === 'online' && ( - - {server.lastPing}ms - - )} -
-
-
- - {/* Specs Tooltip */} - -
-

CPU: {server.specs.cpu}

-

RAM: {server.specs.ram}

-

GPU: {server.specs.gpu}

-

Type: {server.specs.type}

-
-
- - - ))} - -
- - {/* Empty State */} - {servers.length === 0 && ( -
- -

No servers added yet

-

Click the Add Server button to get started

-
- )} -
- - {/* Add Server Modal */} - {isModalOpen && ( -
- +
+
+

Proxmox Dashboard

+ - -
-
-
+ + Add Server +
- )} + +
+ {servers.map(server => ( + + ))} +
+ + {showAddModal && ( + setShowAddModal(false)} + onServerAdded={fetchServers} + /> + )} +
); } diff --git a/src/components/AddServerModal.tsx b/src/components/AddServerModal.tsx new file mode 100644 index 0000000..11d9023 --- /dev/null +++ b/src/components/AddServerModal.tsx @@ -0,0 +1,135 @@ +import React, { useState } from 'react'; +import { X } from 'lucide-react'; +import { supabase } from '../lib/supabase'; + +interface AddServerModalProps { + onClose: () => void; + onServerAdded: () => void; +} + +export function AddServerModal({ onClose, onServerAdded }: AddServerModalProps) { + const [formData, setFormData] = useState({ + name: '', + model: '', + cpu_model: '', + ram_gb: '', + influx_org: '', + influx_token: '', + influx_url: '' + }); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const { error } = await supabase.from('servers').insert([{ + ...formData, + ram_gb: parseInt(formData.ram_gb) + }]); + + if (!error) { + onServerAdded(); + onClose(); + } + }; + + return ( +
+
+
+

Add New Server

+ +
+ +
+
+ + setFormData({ ...formData, name: e.target.value })} + required + /> +
+ +
+ + setFormData({ ...formData, model: e.target.value })} + required + /> +
+ +
+ + setFormData({ ...formData, cpu_model: e.target.value })} + required + /> +
+ +
+ + setFormData({ ...formData, ram_gb: e.target.value })} + required + /> +
+ +
+ + setFormData({ ...formData, influx_org: e.target.value })} + required + /> +
+ +
+ + setFormData({ ...formData, influx_token: e.target.value })} + required + /> +
+ +
+ + setFormData({ ...formData, influx_url: e.target.value })} + required + /> +
+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/ServerCard.tsx b/src/components/ServerCard.tsx new file mode 100644 index 0000000..8c52d40 --- /dev/null +++ b/src/components/ServerCard.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { Server, ServerMetrics } from '../types'; +import { Cpu, MemoryStick as Memory, Server as ServerIcon } from 'lucide-react'; +import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'; + +interface ServerCardProps { + server: Server; + metrics: ServerMetrics[]; +} + +export function ServerCard({ server, metrics }: ServerCardProps) { + const lastMetric = metrics[metrics.length - 1] || { cpu_usage: 0, ram_usage: 0 }; + + return ( +
+
+
+ +

{server.name}

+
+ {server.model} +
+ +
+
+
+ + CPU +
+

{server.cpu_model}

+

{lastMetric.cpu_usage.toFixed(1)}% Usage

+
+ +
+
+ + Memory +
+

{server.ram_gb} GB

+

{lastMetric.ram_usage.toFixed(1)}% Usage

+
+
+ +
+ + + new Date(value).toLocaleTimeString()} + /> + + + + + + +
+
+ ); +} \ No newline at end of file diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..996892e --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,15 @@ +import mysql from 'mysql2/promise'; + +const pool = mysql.createPool({ + host: import.meta.env.VITE_DB_HOST || 'db.pandem.fr', + user: import.meta.env.VITE_DB_USER || 'proxdash', + password: import.meta.env.VITE_DB_PASSWORD || 'proxdash', + database: import.meta.env.VITE_DB_NAME || 'proxdash', + waitForConnections: true, + connectionLimit: 10, + maxIdle: 10, + idleTimeout: 60000, + queueLimit: 0, +}); + +export default pool; \ No newline at end of file diff --git a/src/supabase.ts b/src/lib/supabase.ts similarity index 52% rename from src/supabase.ts rename to src/lib/supabase.ts index 07f3d9b..e071b28 100644 --- a/src/supabase.ts +++ b/src/lib/supabase.ts @@ -1,10 +1,6 @@ import { createClient } from '@supabase/supabase-js'; const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; -const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY; - -if (!supabaseUrl || !supabaseKey) { - throw new Error('Missing Supabase environment variables'); -} +const supabaseKey = import.meta.env.VITE_SUPABASE_KEY; export const supabase = createClient(supabaseUrl, supabaseKey); \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..eabeb2f --- /dev/null +++ b/src/types.ts @@ -0,0 +1,17 @@ +export interface Server { + id: string; + name: string; + model: string; + cpu_model: string; + ram_gb: number; + influx_org: string; + influx_token: string; + influx_url: string; + created_at: string; +} + +export interface ServerMetrics { + cpu_usage: number; + ram_usage: number; + timestamp: string; +} \ No newline at end of file diff --git a/supabase/migrations/20250316211855_navy_oasis.sql b/supabase/migrations/20250316211855_navy_oasis.sql deleted file mode 100644 index e1ea83c..0000000 --- a/supabase/migrations/20250316211855_navy_oasis.sql +++ /dev/null @@ -1,62 +0,0 @@ -/* - # Create servers table for Proxmox dashboard - - 1. New Tables - - `servers` - - `id` (uuid, primary key) - - `name` (text) - - `url` (text) - - `status` (text) - - `specs` (jsonb) - - `last_ping` (integer) - - `user_id` (uuid, foreign key to auth.users) - - `created_at` (timestamp with time zone) - - 2. Security - - Enable RLS on `servers` table - - Add policies for authenticated users to: - - Read their own servers - - Insert their own servers - - Update their own servers - - Delete their own servers -*/ - -CREATE TABLE IF NOT EXISTS servers ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL, - url text NOT NULL, - status text NOT NULL DEFAULT 'checking', - specs jsonb NOT NULL DEFAULT '{}'::jsonb, - last_ping integer, - user_id uuid REFERENCES auth.users(id) NOT NULL, - created_at timestamptz DEFAULT now() NOT NULL -); - --- Enable Row Level Security -ALTER TABLE servers ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can read their own servers" - ON servers - FOR SELECT - TO authenticated - USING (auth.uid() = user_id); - -CREATE POLICY "Users can insert their own servers" - ON servers - FOR INSERT - TO authenticated - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own servers" - ON servers - FOR UPDATE - TO authenticated - USING (auth.uid() = user_id) - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own servers" - ON servers - FOR DELETE - TO authenticated - USING (auth.uid() = user_id); \ No newline at end of file diff --git a/supabase/migrations/20250318001942_shy_cave.sql b/supabase/migrations/20250318001942_shy_cave.sql new file mode 100644 index 0000000..92589be --- /dev/null +++ b/supabase/migrations/20250318001942_shy_cave.sql @@ -0,0 +1,59 @@ +/* + # Create servers table for Proxmox Dashboard + + 1. New Tables + - `servers` + - `id` (uuid, primary key) + - `name` (text, server name) + - `model` (text, server model like Dell R730) + - `cpu_model` (text, CPU model name) + - `ram_gb` (integer, RAM amount in GB) + - `influx_org` (text, InfluxDB organization) + - `influx_token` (text, InfluxDB API token) + - `influx_url` (text, InfluxDB server URL) + - `created_at` (timestamp with timezone) + + 2. Security + - Enable RLS on `servers` table + - Add policies for authenticated users to manage their servers +*/ + +CREATE TABLE servers ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + name text NOT NULL, + model text NOT NULL, + cpu_model text NOT NULL, + ram_gb integer NOT NULL, + influx_org text NOT NULL, + influx_token text NOT NULL, + influx_url text NOT NULL, + created_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security +ALTER TABLE servers ENABLE ROW LEVEL SECURITY; + +-- Create policies +CREATE POLICY "Users can view their servers" + ON servers + FOR SELECT + TO authenticated + USING (true); + +CREATE POLICY "Users can insert their servers" + ON servers + FOR INSERT + TO authenticated + WITH CHECK (true); + +CREATE POLICY "Users can update their servers" + ON servers + FOR UPDATE + TO authenticated + USING (true); + +CREATE POLICY "Users can delete their servers" + ON servers + FOR DELETE + TO authenticated + USING (true); \ No newline at end of file