Add support for database wipe on exit, including delayed deletion handling and user-triggered wipe option in GUI. Update DatabaseManager with sentinel logic and file path utilities.
This commit is contained in:
parent
709c7d0490
commit
992073d43d
@ -33,6 +33,30 @@ DatabaseManager &DatabaseManager::instance() {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString DatabaseManager::dbFilePath() {
|
||||||
|
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||||
|
QString dbPath = settings.value("db/dbPath").toString();
|
||||||
|
if (dbPath.startsWith("~")) {
|
||||||
|
dbPath.replace(0, 1, QDir::homePath());
|
||||||
|
}
|
||||||
|
return dbPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::wipeDatabaseFiles() {
|
||||||
|
const QString dbPath = dbFilePath();
|
||||||
|
QFile::remove(dbPath);
|
||||||
|
QFile::remove(dbPath + "-wal");
|
||||||
|
QFile::remove(dbPath + "-shm");
|
||||||
|
QFile::remove(dbPath + ".wipe");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseManager::writeWipeSentinel() {
|
||||||
|
QFile marker(dbFilePath() + ".wipe");
|
||||||
|
if (marker.open(QIODevice::WriteOnly)) {
|
||||||
|
marker.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QSqlDatabase DatabaseManager::database() {
|
QSqlDatabase DatabaseManager::database() {
|
||||||
QMutexLocker<QRecursiveMutex> locker(&mutex);
|
QMutexLocker<QRecursiveMutex> locker(&mutex);
|
||||||
QString name = connectionName();
|
QString name = connectionName();
|
||||||
@ -40,12 +64,8 @@ QSqlDatabase DatabaseManager::database() {
|
|||||||
// Создаем новое соединение, если его ещё нет
|
// Создаем новое соединение, если его ещё нет
|
||||||
if (!connectionPool.contains(name)) {
|
if (!connectionPool.contains(name)) {
|
||||||
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", name);
|
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", name);
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
|
||||||
|
|
||||||
QString dbPath = settings.value("db/dbPath").toString();
|
const QString dbPath = dbFilePath();
|
||||||
if (dbPath.startsWith("~")) {
|
|
||||||
dbPath.replace(0, 1, QDir::homePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Создаём директорию для БД, если её нет
|
// Создаём директорию для БД, если её нет
|
||||||
QDir().mkpath(QFileInfo(dbPath).absolutePath());
|
QDir().mkpath(QFileInfo(dbPath).absolutePath());
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
class DatabaseManager {
|
class DatabaseManager {
|
||||||
public:
|
public:
|
||||||
@ -15,6 +16,17 @@ public:
|
|||||||
void cleanupOldData(int days = 5);
|
void cleanupOldData(int days = 5);
|
||||||
void closeConnection();
|
void closeConnection();
|
||||||
|
|
||||||
|
// Путь к файлу БД из config.ini (с раскрытием ~). Единая точка вычисления.
|
||||||
|
static QString dbFilePath();
|
||||||
|
// Удалить файл БД вместе с WAL/SHM-сайдкарами и sentinel-файлом.
|
||||||
|
static void wipeDatabaseFiles();
|
||||||
|
// Создать sentinel <dbPath>.wipe — отложенное удаление БД при следующем старте.
|
||||||
|
static void writeWipeSentinel();
|
||||||
|
|
||||||
|
// Флаг «удалить БД при выходе», выставляется из GUI-потока.
|
||||||
|
void setWipeOnExit(bool wipe) { m_wipeOnExit.store(wipe); }
|
||||||
|
bool wipeOnExit() const { return m_wipeOnExit.load(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DatabaseManager();
|
DatabaseManager();
|
||||||
~DatabaseManager();
|
~DatabaseManager();
|
||||||
@ -24,4 +36,5 @@ private:
|
|||||||
QRecursiveMutex mutex;
|
QRecursiveMutex mutex;
|
||||||
QSet<QString> activeConnections;
|
QSet<QString> activeConnections;
|
||||||
QMap<QString, QSqlDatabase> connectionPool;
|
QMap<QString, QSqlDatabase> connectionPool;
|
||||||
|
std::atomic<bool> m_wipeOnExit{false};
|
||||||
};
|
};
|
||||||
25
main.cpp
25
main.cpp
@ -350,6 +350,13 @@ int main(int argc, char *argv[]) {
|
|||||||
QDir().mkpath(QFileInfo(dbPath).absolutePath());
|
QDir().mkpath(QFileInfo(dbPath).absolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Отложенное удаление БД (если файл был занят при прошлом выходе, напр. на Windows).
|
||||||
|
// Делаем здесь, до открытия любого соединения.
|
||||||
|
if (QFile::exists(DatabaseManager::dbFilePath() + ".wipe")) {
|
||||||
|
DatabaseManager::wipeDatabaseFiles();
|
||||||
|
qInfo() << "[main] Локальная БД удалена по запросу пользователя";
|
||||||
|
}
|
||||||
|
|
||||||
// Миграции БД
|
// Миграции БД
|
||||||
DatabaseManager::instance().initSchema();
|
DatabaseManager::instance().initSchema();
|
||||||
DatabaseManager::instance().cleanupOldData(30);
|
DatabaseManager::instance().cleanupOldData(30);
|
||||||
@ -444,5 +451,21 @@ int main(int argc, char *argv[]) {
|
|||||||
loader->close();
|
loader->close();
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
return QApplication::exec();
|
const int rc = QApplication::exec();
|
||||||
|
|
||||||
|
// Пользователь выбрал «Выйти» с удалением БД. Потоки уже остановлены через
|
||||||
|
// aboutToQuit, их соединения сняты. Пробуем удалить файл сразу; если он занят
|
||||||
|
// (Windows: незавершившийся поток) — оставляем sentinel, удалим при старте.
|
||||||
|
if (DatabaseManager::instance().wipeOnExit()) {
|
||||||
|
dbReady.store(false); // прекращаем дозапись логов в БД
|
||||||
|
DatabaseManager::instance().closeConnection();
|
||||||
|
if (QFile::remove(DatabaseManager::dbFilePath())) {
|
||||||
|
DatabaseManager::wipeDatabaseFiles(); // подчистить -wal/-shm/sentinel
|
||||||
|
qInfo() << "[main] Локальная БД удалена при выходе";
|
||||||
|
} else {
|
||||||
|
DatabaseManager::writeWipeSentinel();
|
||||||
|
qInfo() << "[main] Файл БД занят — удаление отложено до следующего старта";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
#include "db/DeviceInfo.h"
|
#include "db/DeviceInfo.h"
|
||||||
#include "device/DeviceWidget.h"
|
#include "device/DeviceWidget.h"
|
||||||
#include "dao/BankProfileDAO.h"
|
#include "dao/BankProfileDAO.h"
|
||||||
|
#include "DatabaseManager.h"
|
||||||
#include "dao/DeviceDAO.h"
|
#include "dao/DeviceDAO.h"
|
||||||
#include "dao/MaterialDAO.h"
|
#include "dao/MaterialDAO.h"
|
||||||
#include "dao/SettingsDAO.h"
|
#include "dao/SettingsDAO.h"
|
||||||
@ -63,11 +64,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
|||||||
auto *tutorialPageAction = new QAction("Инструкция", this);
|
auto *tutorialPageAction = new QAction("Инструкция", this);
|
||||||
auto *eventAction = new QAction("События", this);
|
auto *eventAction = new QAction("События", this);
|
||||||
auto *fileLogAction = new QAction("Логи", this);
|
auto *fileLogAction = new QAction("Логи", this);
|
||||||
|
auto *exitAction = new QAction("Выйти", this);
|
||||||
menu->addAction(devicesPageAction);
|
menu->addAction(devicesPageAction);
|
||||||
// menu->addAction(accountPageAction);
|
// menu->addAction(accountPageAction);
|
||||||
menu->addAction(eventAction);
|
menu->addAction(eventAction);
|
||||||
menu->addAction(tutorialPageAction);
|
menu->addAction(tutorialPageAction);
|
||||||
menu->addAction(fileLogAction);
|
menu->addAction(fileLogAction);
|
||||||
|
menu->addAction(exitAction);
|
||||||
|
|
||||||
connect(devicesPageAction, &QAction::triggered, this, [=]() {
|
connect(devicesPageAction, &QAction::triggered, this, [=]() {
|
||||||
if (!m_devicesWindow) {
|
if (!m_devicesWindow) {
|
||||||
@ -114,6 +117,19 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(exitAction, &QAction::triggered, this, [this]() {
|
||||||
|
QMessageBox msgBox(this);
|
||||||
|
msgBox.setWindowTitle("Выход");
|
||||||
|
msgBox.setText("Удалить локальную базу данных и выйти из приложения?");
|
||||||
|
auto *yesBtn = msgBox.addButton("Выйти", QMessageBox::YesRole);
|
||||||
|
msgBox.addButton("Отмена", QMessageBox::NoRole);
|
||||||
|
msgBox.exec();
|
||||||
|
if (msgBox.clickedButton() == yesBtn) {
|
||||||
|
DatabaseManager::instance().setWipeOnExit(true);
|
||||||
|
qApp->quit(); // штатное завершение фоновых потоков (aboutToQuit)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (SettingsDAO::get("tutorial_shown").isEmpty()) {
|
if (SettingsDAO::get("tutorial_shown").isEmpty()) {
|
||||||
QTimer::singleShot(0, this, [this]() {
|
QTimer::singleShot(0, this, [this]() {
|
||||||
m_tutorialWindow = new TutorialWindow(this);
|
m_tutorialWindow = new TutorialWindow(this);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user