From 245bd477ee36e76879efb771aa4b00d35fdec473 Mon Sep 17 00:00:00 2001 From: Marek Goc Date: Mon, 16 Feb 2026 02:37:31 +0100 Subject: [PATCH] multi dataset --- internal/server/admin_html.go | 8 +- internal/server/database.go | 149 ++++++++++++++++++++++++++++++++++ internal/server/server.go | 26 ++++-- 3 files changed, 174 insertions(+), 9 deletions(-) diff --git a/internal/server/admin_html.go b/internal/server/admin_html.go index 3d5765f..3962418 100644 --- a/internal/server/admin_html.go +++ b/internal/server/admin_html.go @@ -134,9 +134,11 @@ const adminPanelHTML = ` Client + Dataset Snapshot ID Timestamp Size + Storage Type Actions @@ -488,12 +490,14 @@ const adminPanelHTML = ` const sizeGB = (s.size_bytes / (1024*1024*1024)).toFixed(2); return '' + '' + s.client_id + '' + + '' + (s.dataset_name || '-') + '' + '' + s.snapshot_id + '' + '' + new Date(s.timestamp).toLocaleString() + '' + '' + sizeGB + ' GB' + + '' + s.storage_type + '' + '' + - (s.incremental ? 'Incremental' : 'Full') + - (s.compressed ? ' Compressed' : '') + + (s.incremental ? 'Inc' : 'Full') + + (s.compressed ? ' LZ4' : '') + '' + '' + ''; diff --git a/internal/server/database.go b/internal/server/database.go index 45c7d64..ea65761 100644 --- a/internal/server/database.go +++ b/internal/server/database.go @@ -89,6 +89,23 @@ func (d *Database) initTables() error { return fmt.Errorf("failed to create clients table: %v", err) } + // Datasets table - multiple datasets per client + _, err = d.db.Exec(` + CREATE TABLE IF NOT EXISTS datasets ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + client_id TEXT NOT NULL, + dataset_name TEXT NOT NULL, + storage_type TEXT NOT NULL DEFAULT 's3', + enabled INTEGER NOT NULL DEFAULT 1, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (client_id) REFERENCES clients(client_id) ON DELETE CASCADE, + UNIQUE(client_id, dataset_name) + ) + `) + if err != nil { + return fmt.Errorf("failed to create datasets table: %v", err) + } + // Snapshots table _, err = d.db.Exec(` CREATE TABLE IF NOT EXISTS snapshots ( @@ -400,6 +417,138 @@ func (d *Database) CreateDefaultClient() error { return d.SaveClient(defaultClient) } +// CreateDefaultDataset creates a default dataset for a client if none exists +func (d *Database) CreateDefaultDataset(clientID, datasetName string) error { + datasets, err := d.GetDatasetsByClient(clientID) + if err != nil { + return err + } + + if len(datasets) > 0 { + return nil + } + + // Create default dataset + dataset := &DatasetConfig{ + ClientID: clientID, + DatasetName: datasetName, + StorageType: "s3", + Enabled: true, + } + + return d.SaveDataset(dataset) +} + +// DatasetConfig represents a dataset configuration +type DatasetConfig struct { + ID int64 `json:"id"` + ClientID string `json:"client_id"` + DatasetName string `json:"dataset_name"` + StorageType string `json:"storage_type"` + Enabled bool `json:"enabled"` +} + +// GetDatasetsByClient gets all datasets for a client +func (d *Database) GetDatasetsByClient(clientID string) ([]*DatasetConfig, error) { + query := `SELECT id, client_id, dataset_name, storage_type, enabled FROM datasets WHERE client_id = ?` + + rows, err := d.db.Query(query, clientID) + if err != nil { + return nil, err + } + defer rows.Close() + + var datasets []*DatasetConfig + for rows.Next() { + dataset := &DatasetConfig{} + var enabled int + + err := rows.Scan(&dataset.ID, &dataset.ClientID, &dataset.DatasetName, &dataset.StorageType, &enabled) + if err != nil { + return nil, err + } + + dataset.Enabled = enabled == 1 + datasets = append(datasets, dataset) + } + + return datasets, nil +} + +// GetDatasetByName gets a dataset by client and dataset name +func (d *Database) GetDatasetByName(clientID, datasetName string) (*DatasetConfig, error) { + query := `SELECT id, client_id, dataset_name, storage_type, enabled FROM datasets WHERE client_id = ? AND dataset_name = ?` + + row := d.db.QueryRow(query, clientID, datasetName) + + dataset := &DatasetConfig{} + var enabled int + + err := row.Scan(&dataset.ID, &dataset.ClientID, &dataset.DatasetName, &dataset.StorageType, &enabled) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + + dataset.Enabled = enabled == 1 + return dataset, nil +} + +// SaveDataset saves or updates a dataset +func (d *Database) SaveDataset(dataset *DatasetConfig) error { + enabled := 0 + if dataset.Enabled { + enabled = 1 + } + + if dataset.ID == 0 { + // Insert new + _, err := d.db.Exec(`INSERT INTO datasets (client_id, dataset_name, storage_type, enabled) VALUES (?, ?, ?, ?)`, + dataset.ClientID, dataset.DatasetName, dataset.StorageType, enabled) + return err + } + + // Update existing + _, err := d.db.Exec(`UPDATE datasets SET storage_type = ?, enabled = ? WHERE id = ?`, + dataset.StorageType, enabled, dataset.ID) + return err +} + +// DeleteDataset deletes a dataset +func (d *Database) DeleteDataset(id int64) error { + _, err := d.db.Exec(`DELETE FROM datasets WHERE id = ?`, id) + return err +} + +// GetAllDatasets gets all datasets +func (d *Database) GetAllDatasets() ([]*DatasetConfig, error) { + query := `SELECT id, client_id, dataset_name, storage_type, enabled FROM datasets` + + rows, err := d.db.Query(query) + if err != nil { + return nil, err + } + defer rows.Close() + + var datasets []*DatasetConfig + for rows.Next() { + dataset := &DatasetConfig{} + var enabled int + + err := rows.Scan(&dataset.ID, &dataset.ClientID, &dataset.DatasetName, &dataset.StorageType, &enabled) + if err != nil { + return nil, err + } + + dataset.Enabled = enabled == 1 + datasets = append(datasets, dataset) + } + + return datasets, nil +} + // GetSnapshotByID retrieves a specific snapshot func (d *Database) GetSnapshotByID(clientID, snapshotID string) (*SnapshotMetadata, error) { snap := &SnapshotMetadata{} diff --git a/internal/server/server.go b/internal/server/server.go index a85c7dd..61601bf 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -271,6 +271,25 @@ func (s *Server) HandleUploadStream(w http.ResponseWriter, r *http.Request) { return } + // Check if dataset is allowed for this client + dataset, err := s.db.GetDatasetByName(clientID, datasetName) + if err != nil || dataset == nil { + log.Printf("Dataset %s not found for client %s", datasetName, clientID) + respondJSON(w, http.StatusForbidden, UploadResponse{ + Success: false, + Message: "Dataset not configured for this client", + }) + return + } + + if !dataset.Enabled { + respondJSON(w, http.StatusForbidden, UploadResponse{ + Success: false, + Message: "Dataset is disabled", + }) + return + } + ctx := context.Background() // Upload to S3 @@ -438,13 +457,6 @@ func (s *Server) HandleDownload(w http.ResponseWriter, r *http.Request) { return } - // Find snapshot metadata - client, err := s.db.GetClient(clientID) - if err != nil || client == nil { - http.Error(w, "Client not found", http.StatusNotFound) - return - } - targetSnapshot, err := s.db.GetSnapshotByID(clientID, snapshotID) if err != nil || targetSnapshot == nil { http.Error(w, "Snapshot not found", http.StatusNotFound)