mouai pt
This commit is contained in:
parent
dd60497e5b
commit
aa6da1d02e
6 changed files with 111 additions and 172 deletions
128
package-lock.json
generated
128
package-lock.json
generated
|
@ -8,7 +8,6 @@
|
|||
"name": "proxmox-dashboard",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.39.7",
|
||||
"lucide-react": "^0.344.0",
|
||||
"mysql2": "^3.9.2",
|
||||
"react": "^18.3.1",
|
||||
|
@ -1217,73 +1216,6 @@
|
|||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@supabase/auth-js": {
|
||||
"version": "2.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.68.0.tgz",
|
||||
"integrity": "sha512-odG7nb7aOmZPUXk6SwL2JchSsn36Ppx11i2yWMIc/meUO2B2HK9YwZHPK06utD9Ql9ke7JKDbwGin/8prHKxxQ==",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/functions-js": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz",
|
||||
"integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/node-fetch": {
|
||||
"version": "2.6.15",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
|
||||
"integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/postgrest-js": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.2.tgz",
|
||||
"integrity": "sha512-MXRbk4wpwhWl9IN6rIY1mR8uZCCG4MZAEji942ve6nMwIqnBgBnZhZlON6zTTs6fgveMnoCILpZv1+K91jN+ow==",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/realtime-js": {
|
||||
"version": "2.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz",
|
||||
"integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14",
|
||||
"@types/phoenix": "^1.5.4",
|
||||
"@types/ws": "^8.5.10",
|
||||
"ws": "^8.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/storage-js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz",
|
||||
"integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==",
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/supabase-js": {
|
||||
"version": "2.49.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.1.tgz",
|
||||
"integrity": "sha512-lKaptKQB5/juEF5+jzmBeZlz69MdHZuxf+0f50NwhL+IE//m4ZnOeWlsKRjjsM0fVayZiQKqLvYdBn0RLkhGiQ==",
|
||||
"dependencies": {
|
||||
"@supabase/auth-js": "2.68.0",
|
||||
"@supabase/functions-js": "2.4.4",
|
||||
"@supabase/node-fetch": "2.6.15",
|
||||
"@supabase/postgrest-js": "1.19.2",
|
||||
"@supabase/realtime-js": "2.11.2",
|
||||
"@supabase/storage-js": "2.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
|
@ -1395,15 +1327,13 @@
|
|||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/phoenix": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz",
|
||||
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A=="
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
|
||||
|
@ -1429,14 +1359,6 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz",
|
||||
|
@ -4218,11 +4140,6 @@
|
|||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
|
||||
|
@ -4292,7 +4209,10 @@
|
|||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.1",
|
||||
|
@ -4419,20 +4339,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
@ -4578,26 +4484,6 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.39.7",
|
||||
"lucide-react": "^0.344.0",
|
||||
"mysql2": "^3.9.2",
|
||||
"react": "^18.3.1",
|
||||
|
|
22
src/App.tsx
22
src/App.tsx
|
@ -3,7 +3,7 @@ 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';
|
||||
import { getServers } from './lib/db';
|
||||
|
||||
function App() {
|
||||
const [servers, setServers] = useState<Server[]>([]);
|
||||
|
@ -15,20 +15,22 @@ function App() {
|
|||
}, []);
|
||||
|
||||
const fetchServers = async () => {
|
||||
const { data } = await supabase
|
||||
.from('servers')
|
||||
.select('*')
|
||||
.order('created_at', { ascending: true });
|
||||
|
||||
if (data) {
|
||||
setServers(data);
|
||||
data.forEach(server => {
|
||||
fetchMetrics(server);
|
||||
try {
|
||||
const data = await getServers();
|
||||
setServers(data as Server[]);
|
||||
data.forEach((server: Server) => {
|
||||
if (server.influx_enabled) {
|
||||
fetchMetrics(server);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch servers:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchMetrics = async (server: Server) => {
|
||||
if (!server.influx_enabled) return;
|
||||
|
||||
// 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) => ({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { X } from 'lucide-react';
|
||||
import { supabase } from '../lib/supabase';
|
||||
import { addServer } from '../lib/db';
|
||||
|
||||
interface AddServerModalProps {
|
||||
onClose: () => void;
|
||||
|
@ -13,6 +13,7 @@ export function AddServerModal({ onClose, onServerAdded }: AddServerModalProps)
|
|||
model: '',
|
||||
cpu_model: '',
|
||||
ram_gb: '',
|
||||
influx_enabled: false,
|
||||
influx_org: '',
|
||||
influx_token: '',
|
||||
influx_url: ''
|
||||
|
@ -21,14 +22,16 @@ export function AddServerModal({ onClose, onServerAdded }: AddServerModalProps)
|
|||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { error } = await supabase.from('servers').insert([{
|
||||
...formData,
|
||||
ram_gb: parseInt(formData.ram_gb)
|
||||
}]);
|
||||
|
||||
if (!error) {
|
||||
try {
|
||||
await addServer({
|
||||
...formData,
|
||||
ram_gb: parseInt(formData.ram_gb)
|
||||
});
|
||||
onServerAdded();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Failed to add server:', error);
|
||||
alert('Failed to add server. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -88,39 +91,56 @@ export function AddServerModal({ onClose, onServerAdded }: AddServerModalProps)
|
|||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300">InfluxDB Organization</label>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="text"
|
||||
className="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"
|
||||
value={formData.influx_org}
|
||||
onChange={(e) => setFormData({ ...formData, influx_org: e.target.value })}
|
||||
required
|
||||
type="checkbox"
|
||||
id="influxEnabled"
|
||||
className="rounded bg-gray-700 border-gray-600 text-blue-600"
|
||||
checked={formData.influx_enabled}
|
||||
onChange={(e) => setFormData({ ...formData, influx_enabled: e.target.checked })}
|
||||
/>
|
||||
<label htmlFor="influxEnabled" className="text-sm font-medium text-gray-300">
|
||||
Enable InfluxDB Metrics
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300">InfluxDB Token</label>
|
||||
<input
|
||||
type="password"
|
||||
className="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"
|
||||
value={formData.influx_token}
|
||||
onChange={(e) => setFormData({ ...formData, influx_token: e.target.value })}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300">InfluxDB URL</label>
|
||||
<input
|
||||
type="url"
|
||||
className="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"
|
||||
value={formData.influx_url}
|
||||
onChange={(e) => setFormData({ ...formData, influx_url: e.target.value })}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{formData.influx_enabled && (
|
||||
<>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300">InfluxDB Organization</label>
|
||||
<input
|
||||
type="text"
|
||||
className="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"
|
||||
value={formData.influx_org}
|
||||
onChange={(e) => setFormData({ ...formData, influx_org: e.target.value })}
|
||||
required={formData.influx_enabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300">InfluxDB Token</label>
|
||||
<input
|
||||
type="password"
|
||||
className="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"
|
||||
value={formData.influx_token}
|
||||
onChange={(e) => setFormData({ ...formData, influx_token: e.target.value })}
|
||||
required={formData.influx_enabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300">InfluxDB URL</label>
|
||||
<input
|
||||
type="url"
|
||||
className="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"
|
||||
value={formData.influx_url}
|
||||
onChange={(e) => setFormData({ ...formData, influx_url: e.target.value })}
|
||||
required={formData.influx_enabled}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
|
|
|
@ -12,4 +12,35 @@ const pool = mysql.createPool({
|
|||
queueLimit: 0,
|
||||
});
|
||||
|
||||
export async function getServers() {
|
||||
const [rows] = await pool.query('SELECT * FROM servers ORDER BY created_at ASC');
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function addServer(server: {
|
||||
name: string;
|
||||
model: string;
|
||||
cpu_model: string;
|
||||
ram_gb: number;
|
||||
influx_enabled: boolean;
|
||||
influx_org?: string;
|
||||
influx_token?: string;
|
||||
influx_url?: string;
|
||||
}) {
|
||||
const [result] = await pool.query(
|
||||
'INSERT INTO servers (name, model, cpu_model, ram_gb, influx_enabled, influx_org, influx_token, influx_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
server.name,
|
||||
server.model,
|
||||
server.cpu_model,
|
||||
server.ram_gb,
|
||||
server.influx_enabled,
|
||||
server.influx_org || null,
|
||||
server.influx_token || null,
|
||||
server.influx_url || null,
|
||||
]
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
export default pool;
|
|
@ -1,12 +1,13 @@
|
|||
export interface Server {
|
||||
id: string;
|
||||
id: number;
|
||||
name: string;
|
||||
model: string;
|
||||
cpu_model: string;
|
||||
ram_gb: number;
|
||||
influx_org: string;
|
||||
influx_token: string;
|
||||
influx_url: string;
|
||||
influx_enabled: boolean;
|
||||
influx_org: string | null;
|
||||
influx_token: string | null;
|
||||
influx_url: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue