multi dataset
This commit is contained in:
@@ -134,9 +134,11 @@ const adminPanelHTML = `<!DOCTYPE html>
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Client</th>
|
<th>Client</th>
|
||||||
|
<th>Dataset</th>
|
||||||
<th>Snapshot ID</th>
|
<th>Snapshot ID</th>
|
||||||
<th>Timestamp</th>
|
<th>Timestamp</th>
|
||||||
<th>Size</th>
|
<th>Size</th>
|
||||||
|
<th>Storage</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -488,12 +490,14 @@ const adminPanelHTML = `<!DOCTYPE html>
|
|||||||
const sizeGB = (s.size_bytes / (1024*1024*1024)).toFixed(2);
|
const sizeGB = (s.size_bytes / (1024*1024*1024)).toFixed(2);
|
||||||
return '<tr>' +
|
return '<tr>' +
|
||||||
'<td>' + s.client_id + '</td>' +
|
'<td>' + s.client_id + '</td>' +
|
||||||
|
'<td>' + (s.dataset_name || '-') + '</td>' +
|
||||||
'<td>' + s.snapshot_id + '</td>' +
|
'<td>' + s.snapshot_id + '</td>' +
|
||||||
'<td>' + new Date(s.timestamp).toLocaleString() + '</td>' +
|
'<td>' + new Date(s.timestamp).toLocaleString() + '</td>' +
|
||||||
'<td>' + sizeGB + ' GB</td>' +
|
'<td>' + sizeGB + ' GB</td>' +
|
||||||
|
'<td><span class="badge ' + (s.storage_type === 's3' ? 'badge-info' : 'badge-warning') + '">' + s.storage_type + '</span></td>' +
|
||||||
'<td>' +
|
'<td>' +
|
||||||
(s.incremental ? '<span class="badge badge-info">Incremental</span>' : '<span class="badge badge-success">Full</span>') +
|
(s.incremental ? '<span class="badge badge-info">Inc</span>' : '<span class="badge badge-success">Full</span>') +
|
||||||
(s.compressed ? ' <span class="badge badge-info">Compressed</span>' : '') +
|
(s.compressed ? ' <span class="badge badge-info">LZ4</span>' : '') +
|
||||||
'</td>' +
|
'</td>' +
|
||||||
'<td><button class="btn btn-sm btn-danger" onclick="deleteSnapshot(\'' + s.client_id + '\', \'' + s.snapshot_id + '\')">Delete</button></td>' +
|
'<td><button class="btn btn-sm btn-danger" onclick="deleteSnapshot(\'' + s.client_id + '\', \'' + s.snapshot_id + '\')">Delete</button></td>' +
|
||||||
'</tr>';
|
'</tr>';
|
||||||
|
|||||||
@@ -89,6 +89,23 @@ func (d *Database) initTables() error {
|
|||||||
return fmt.Errorf("failed to create clients table: %v", err)
|
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
|
// Snapshots table
|
||||||
_, err = d.db.Exec(`
|
_, err = d.db.Exec(`
|
||||||
CREATE TABLE IF NOT EXISTS snapshots (
|
CREATE TABLE IF NOT EXISTS snapshots (
|
||||||
@@ -400,6 +417,138 @@ func (d *Database) CreateDefaultClient() error {
|
|||||||
return d.SaveClient(defaultClient)
|
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
|
// GetSnapshotByID retrieves a specific snapshot
|
||||||
func (d *Database) GetSnapshotByID(clientID, snapshotID string) (*SnapshotMetadata, error) {
|
func (d *Database) GetSnapshotByID(clientID, snapshotID string) (*SnapshotMetadata, error) {
|
||||||
snap := &SnapshotMetadata{}
|
snap := &SnapshotMetadata{}
|
||||||
|
|||||||
@@ -271,6 +271,25 @@ func (s *Server) HandleUploadStream(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Upload to S3
|
// Upload to S3
|
||||||
@@ -438,13 +457,6 @@ func (s *Server) HandleDownload(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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)
|
targetSnapshot, err := s.db.GetSnapshotByID(clientID, snapshotID)
|
||||||
if err != nil || targetSnapshot == nil {
|
if err != nil || targetSnapshot == nil {
|
||||||
http.Error(w, "Snapshot not found", http.StatusNotFound)
|
http.Error(w, "Snapshot not found", http.StatusNotFound)
|
||||||
|
|||||||
Reference in New Issue
Block a user