From 98918d33b36119f2f5a13e15022f229004c5dc50 Mon Sep 17 00:00:00 2001 From: ByakuraRinne Date: Sun, 16 Mar 2025 22:36:59 +0100 Subject: [PATCH] db de con a changer --- .env | 3 + package-lock.json | 133 ++++++++++++++++++ package.json | 1 + src/App.tsx | 88 ++++++++++-- src/supabase.ts | 10 ++ .../migrations/20250316211855_navy_oasis.sql | 62 ++++++++ 6 files changed, 286 insertions(+), 11 deletions(-) create mode 100644 .env create mode 100644 src/supabase.ts create mode 100644 supabase/migrations/20250316211855_navy_oasis.sql diff --git a/.env b/.env new file mode 100644 index 0000000..2aa6918 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ + +VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZwZmJzY2psanBtcGJjb2hteHJtIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDIxNTk1NjIsImV4cCI6MjA1NzczNTU2Mn0.bMHEDr8XS2fFVr38JuCJOF80sWU3P4isQ1DStRuwUiw +VITE_SUPABASE_URL=https://vpfbscjljpmpbcohmxrm.supabase.co \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f9acd72..62446ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "vite-react-typescript-starter", "version": "0.0.0", "dependencies": { + "@supabase/supabase-js": "^2.39.7", "framer-motion": "^11.0.8", "lucide-react": "^0.344.0", "react": "^18.3.1", @@ -1204,6 +1205,73 @@ "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", @@ -1257,6 +1325,19 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "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", @@ -1282,6 +1363,14 @@ "@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", @@ -3742,6 +3831,11 @@ "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", @@ -3813,6 +3907,11 @@ } } }, + "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==" + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -3917,6 +4016,20 @@ } } }, + "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", @@ -4062,6 +4175,26 @@ "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", diff --git a/package.json b/package.json index 6858d86..95968d2 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@supabase/supabase-js": "^2.39.7", "lucide-react": "^0.344.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/src/App.tsx b/src/App.tsx index 21cdf72..d1ee512 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ 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 }) => ( @@ -45,6 +46,28 @@ function App() { } }); + // 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(); + }, []); + const pingServer = async (server: ProxmoxServer) => { setServers(current => current.map(s => @@ -56,15 +79,34 @@ function App() { 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: endTime - startTime } + ? { ...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 @@ -73,14 +115,28 @@ function App() { } }; - const addServer = () => { + 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 = { - id: Date.now().toString(), - name: newServer.name, - url: newServer.url, - status: 'checking', - specs: newServer.specs + ...serverData, + specs: serverData.specs, + lastPing: serverData.last_ping }; setServers([...servers, server]); @@ -94,7 +150,17 @@ function App() { } }; - const deleteServer = (id: string) => { + const deleteServer = async (id: string) => { + const { error } = await supabase + .from('servers') + .delete() + .eq('id', id); + + if (error) { + console.error('Error deleting server:', error); + return; + } + setServers(servers.filter(server => server.id !== id)); }; @@ -190,8 +256,8 @@ function App() {

CPU: {server.specs.cpu}

@@ -199,7 +265,7 @@ function App() {

GPU: {server.specs.gpu}

Type: {server.specs.type}

-
diff --git a/src/supabase.ts b/src/supabase.ts new file mode 100644 index 0000000..07f3d9b --- /dev/null +++ b/src/supabase.ts @@ -0,0 +1,10 @@ +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'); +} + +export const supabase = createClient(supabaseUrl, supabaseKey); \ No newline at end of file diff --git a/supabase/migrations/20250316211855_navy_oasis.sql b/supabase/migrations/20250316211855_navy_oasis.sql new file mode 100644 index 0000000..e1ea83c --- /dev/null +++ b/supabase/migrations/20250316211855_navy_oasis.sql @@ -0,0 +1,62 @@ +/* + # 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