initial commit
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
namespace radiantwave::db {
|
||||
|
||||
struct ProclamationRecord {
|
||||
std::string id;
|
||||
std::string title;
|
||||
std::string content;
|
||||
double solfeggio_freq_hz = 528.0;
|
||||
std::string category = "general";
|
||||
bool is_active = true;
|
||||
bool selected = false;
|
||||
int category_id = 0; // maps to ProclamationCategoryRecord::id
|
||||
int solfeggio_frequency_id = 0; // maps to the solfeggio frequency preset
|
||||
int required_level_id = 1; // minimum SubscriptionLevel id to unlock it
|
||||
};
|
||||
|
||||
struct ProclamationCategoryRecord {
|
||||
int id = 0;
|
||||
std::string name;
|
||||
int parent_id = 0; // 0 = root category
|
||||
int sort_order = 0;
|
||||
};
|
||||
|
||||
struct AssetDownloadRecord {
|
||||
std::string id;
|
||||
std::string filename;
|
||||
std::string url;
|
||||
std::string status = "pending"; // pending, downloading, completed, failed
|
||||
int64_t bytes_downloaded = 0;
|
||||
int64_t total_bytes = 0;
|
||||
std::string error_message = "";
|
||||
};
|
||||
|
||||
struct AudioFileRecord {
|
||||
int id = 0;
|
||||
std::string display_name;
|
||||
std::string filename;
|
||||
int display_order = 0;
|
||||
int solfeggio_frequency_id = 0; // 0 = not associated with a solfeggio frequency
|
||||
};
|
||||
|
||||
// A single color in a solfeggio frequency's color palette (RGBA).
|
||||
struct PaletteColor {
|
||||
uint8_t r = 0;
|
||||
uint8_t g = 0;
|
||||
uint8_t b = 0;
|
||||
uint8_t a = 255;
|
||||
};
|
||||
|
||||
// The ordered palette for one solfeggio frequency (as stored in the
|
||||
// color_palettes table, keyed by color_index).
|
||||
struct PaletteRecord {
|
||||
int solfeggio_frequency_id = 0;
|
||||
std::vector<PaletteColor> colors;
|
||||
};
|
||||
|
||||
class DatabaseManager {
|
||||
public:
|
||||
DatabaseManager();
|
||||
~DatabaseManager();
|
||||
|
||||
// Non-copyable
|
||||
DatabaseManager(const DatabaseManager&) = delete;
|
||||
DatabaseManager& operator=(const DatabaseManager&) = delete;
|
||||
|
||||
// Initialize & open database at path (default: ~/.local/radiantwave/radiantwave.db)
|
||||
bool initialize(const std::string& custom_path = "");
|
||||
|
||||
// Close database connection
|
||||
void close();
|
||||
|
||||
// True once initialize() has opened the database.
|
||||
bool isOpen() const;
|
||||
|
||||
// ── Key-Value Storage (system_kv table) ───────────────────────────
|
||||
bool setKV(const std::string& key, const std::string& value);
|
||||
std::string getKV(const std::string& key, const std::string& defaultValue = "") const;
|
||||
bool hasKV(const std::string& key);
|
||||
bool deleteKV(const std::string& key);
|
||||
|
||||
// ── Proclamations Table ───────────────────────────────────────────
|
||||
bool saveProclamation(const ProclamationRecord& record);
|
||||
std::vector<ProclamationRecord> getActiveProclamations();
|
||||
|
||||
// Full sync: replaces all categories with the given set.
|
||||
bool replaceCategories(const std::vector<ProclamationCategoryRecord>& records);
|
||||
|
||||
// Full sync: upserts proclamations, deletes any not in `records`,
|
||||
// preserves the existing `selected` state, and forces the proclamation
|
||||
// identified by `defaultProclamationId` to remain selected.
|
||||
bool syncProclamations(const std::vector<ProclamationRecord>& records,
|
||||
const std::string& defaultProclamationId);
|
||||
|
||||
// Fetch all proclamations (regardless of is_active).
|
||||
std::vector<ProclamationRecord> getAllProclamations();
|
||||
|
||||
// Fetch all categories ordered by sort_order then id.
|
||||
std::vector<ProclamationCategoryRecord> getAllCategories();
|
||||
|
||||
// Toggle a single proclamation's selected flag.
|
||||
bool setProclamationSelected(const std::string& id, bool selected);
|
||||
|
||||
// Atomically replace the selection with `selectedIds`, then force the
|
||||
// default proclamation to remain selected.
|
||||
bool saveSelectedProclamations(const std::vector<std::string>& selectedIds,
|
||||
const std::string& defaultProclamationId);
|
||||
|
||||
// Force the default proclamation to be flagged selected in the database.
|
||||
bool ensureDefaultProclamationSelected(const std::string& defaultProclamationId);
|
||||
|
||||
// ── Asset Downloads Table ─────────────────────────────────────────
|
||||
bool updateAssetDownload(const AssetDownloadRecord& record);
|
||||
std::vector<AssetDownloadRecord> getPendingAssetDownloads();
|
||||
|
||||
// ── Audio Files Table ─────────────────────────────────────────────
|
||||
// Reconciles audio_files to the given set (the on-disk audio files).
|
||||
// Upserts by filename with the manifest as source of truth for
|
||||
// display_name / display_order / solfeggio_frequency_id, and deletes
|
||||
// any row whose filename is not in `records`.
|
||||
bool syncAudioFiles(const std::vector<AudioFileRecord>& records);
|
||||
std::vector<AudioFileRecord> getAudioFiles();
|
||||
|
||||
// ── Color Palettes Table ──────────────────────────────────────────
|
||||
// Returns the 7-color palette for a solfeggio frequency id, ordered by
|
||||
// color_index. Falls back to the 528 Hz palette when the id has no rows.
|
||||
std::vector<PaletteColor> getPaletteForFrequencyId(int solfeggio_frequency_id);
|
||||
|
||||
// Full sync: replaces the entire color_palettes table with `records`
|
||||
// (the portal is the source of truth for palettes). Pass an empty vector
|
||||
// to clear all palettes. Colors are stored as "#RRGGBB" hex strings.
|
||||
//
|
||||
// NOTE: not currently called from the app flow. This is readiness code so
|
||||
// palettes can be pushed from the portal once the API route exists.
|
||||
bool replaceColorPalettes(const std::vector<PaletteRecord>& records);
|
||||
|
||||
// ── Event Logging Table ───────────────────────────────────────────
|
||||
bool logEvent(const std::string& level, const std::string& component, const std::string& message);
|
||||
|
||||
private:
|
||||
std::string resolvePath(const std::string& raw_path) const;
|
||||
bool runMigrations();
|
||||
bool executeSql(const std::string& sql);
|
||||
bool columnExists(const std::string& table, const std::string& column);
|
||||
bool ensureColumn(const std::string& table, const std::string& column,
|
||||
const std::string& column_ddl);
|
||||
void seedColorPalettes();
|
||||
void bindProclamationRecord(sqlite3_stmt* stmt, const ProclamationRecord& record);
|
||||
bool ensureDefaultProclamationSelectedUnlocked(const std::string& defaultProclamationId);
|
||||
|
||||
sqlite3* db_ = nullptr;
|
||||
std::string db_path_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace radiantwave::db
|
||||
Reference in New Issue
Block a user