License system: grace period 7 days, blocked state, license server API, remove YouTube publisher, C++20

This commit is contained in:
Karuma 2026-06-27 18:36:37 +03:00
parent 652f44fe7e
commit 5c9555eec1
5 changed files with 125 additions and 13 deletions

View File

@ -176,6 +176,34 @@ void UBLicenseDialog::updateInfo()
mTrialButton->setEnabled(false); mTrialButton->setEnabled(false);
break; break;
case UBLicenseManager::LicenseGracePeriod:
mTitleLabel->setText(tr("Льготный период"));
mInfoLabel->setText(
tr("Лицензия истекла. Действует льготный период.\n"
"Осталось дней: %1 из %2\n"
"После окончания льготного периода приложение будет заблокировано.\n"
"Введите лицензионный ключ для продолжения работы.")
.arg(lm->gracePeriodDaysRemaining())
.arg(lm->gracePeriodDurationDays())
);
mKeyEdit->setEnabled(true);
mActivateButton->setEnabled(true);
mTrialButton->setEnabled(false);
break;
case UBLicenseManager::LicenseBlocked:
mTitleLabel->setText(tr("Приложение заблокировано"));
mInfoLabel->setText(
tr("Льготный период истёк.\n"
"Приложение заблокировано.\n"
"Введите лицензионный ключ для разблокировки.\n"
"Минимальная стоимость лицензии: 5 000 ₽")
);
mKeyEdit->setEnabled(true);
mActivateButton->setEnabled(true);
mTrialButton->setEnabled(false);
break;
default: default:
mTitleLabel->setText(tr("Лицензия не активирована")); mTitleLabel->setText(tr("Лицензия не активирована"));
mInfoLabel->setText( mInfoLabel->setText(

View File

@ -12,6 +12,7 @@
#include "UBLicenseManager.h" #include "UBLicenseManager.h"
#include <QApplication>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QMessageAuthenticationCode> #include <QMessageAuthenticationCode>
#include <QCryptographicHash> #include <QCryptographicHash>
@ -25,11 +26,12 @@ UBLicenseManager* UBLicenseManager::sInstance = nullptr;
const QString LICENSE_SERVER_URL = "https://license.saicore.ru/api/activate"; const QString LICENSE_SERVER_URL = "https://license.saicore.ru/api/activate";
const int TRIAL_DURATION_DAYS = 30; const int TRIAL_DURATION_DAYS = 30;
const int GRACE_PERIOD_DAYS = 7;
UBLicenseManager* UBLicenseManager::instance() UBLicenseManager* UBLicenseManager::instance()
{ {
if (!sInstance) if (!sInstance)
sInstance = new UBLicenseManager(qApp); sInstance = new UBLicenseManager(QApplication::instance());
return sInstance; return sInstance;
} }
@ -91,6 +93,7 @@ void UBLicenseManager::loadState()
in >> mLicenseKey; in >> mLicenseKey;
in >> mTrialStart; in >> mTrialStart;
in >> mLicenseExpiry; in >> mLicenseExpiry;
in >> mGracePeriodStart;
file.close(); file.close();
} }
@ -108,6 +111,7 @@ void UBLicenseManager::saveState()
out << mLicenseKey; out << mLicenseKey;
out << mTrialStart; out << mTrialStart;
out << mLicenseExpiry; out << mLicenseExpiry;
out << mGracePeriodStart;
file.close(); file.close();
} }
@ -121,7 +125,20 @@ void UBLicenseManager::updateState()
} }
else else
{ {
mState = LicenseExpired; if (!mGracePeriodStart.isValid())
{
mGracePeriodStart = QDateTime::currentDateTime();
saveState();
}
QDateTime graceEnd = mGracePeriodStart.addDays(GRACE_PERIOD_DAYS);
if (QDateTime::currentDateTime() < graceEnd)
{
mState = LicenseGracePeriod;
}
else
{
mState = LicenseBlocked;
}
} }
} }
else if (mTrialStart.isValid()) else if (mTrialStart.isValid())
@ -133,7 +150,20 @@ void UBLicenseManager::updateState()
} }
else else
{ {
mState = LicenseExpired; if (!mGracePeriodStart.isValid())
{
mGracePeriodStart = QDateTime::currentDateTime();
saveState();
}
QDateTime graceEnd = mGracePeriodStart.addDays(GRACE_PERIOD_DAYS);
if (QDateTime::currentDateTime() < graceEnd)
{
mState = LicenseGracePeriod;
}
else
{
mState = LicenseBlocked;
}
} }
} }
else else
@ -188,7 +218,7 @@ int UBLicenseManager::trialDaysRemaining() const
bool UBLicenseManager::isLicensed() const bool UBLicenseManager::isLicensed() const
{ {
return mState == LicenseActive; return mState == LicenseActive || mState == LicenseTrial || mState == LicenseGracePeriod;
} }
bool UBLicenseManager::isTrialActive() const bool UBLicenseManager::isTrialActive() const
@ -196,6 +226,33 @@ bool UBLicenseManager::isTrialActive() const
return mState == LicenseTrial; return mState == LicenseTrial;
} }
bool UBLicenseManager::isBlocked() const
{
return mState == LicenseBlocked;
}
bool UBLicenseManager::isGracePeriod() const
{
return mState == LicenseGracePeriod;
}
int UBLicenseManager::gracePeriodDurationDays() const
{
return GRACE_PERIOD_DAYS;
}
int UBLicenseManager::gracePeriodDaysRemaining() const
{
if (!mGracePeriodStart.isValid())
return 0;
QDateTime graceEnd = mGracePeriodStart.addDays(GRACE_PERIOD_DAYS);
qint64 secs = QDateTime::currentDateTime().secsTo(graceEnd);
if (secs <= 0)
return 0;
return static_cast<int>(secs / 86400);
}
bool UBLicenseManager::validateLicenseFormat(const QString& key) const bool UBLicenseManager::validateLicenseFormat(const QString& key) const
{ {
QString trimmed = key.trimmed().toUpper(); QString trimmed = key.trimmed().toUpper();
@ -255,7 +312,7 @@ bool UBLicenseManager::activateLicense(const QString& key)
json["version"] = "1.0.0"; json["version"] = "1.0.0";
QNetworkReply* reply = mNetworkManager->post(request, QJsonDocument(json).toJson(QJsonDocument::Compact)); QNetworkReply* reply = mNetworkManager->post(request, QJsonDocument(json).toJson(QJsonDocument::Compact));
connect(reply, &QNetworkReply::finished, this, &UBLicenseManager::onActivationReply); connect(reply, &QNetworkReply::finished, this, [this, reply]() { onActivationReply(reply); });
return true; return true;
} }
@ -316,6 +373,7 @@ void UBLicenseManager::startTrial()
if (mState == LicenseNone) if (mState == LicenseNone)
{ {
mTrialStart = QDateTime::currentDateTime(); mTrialStart = QDateTime::currentDateTime();
mGracePeriodStart = QDateTime();
mState = LicenseTrial; mState = LicenseTrial;
saveState(); saveState();
emit licenseStateChanged(mState); emit licenseStateChanged(mState);
@ -326,6 +384,7 @@ void UBLicenseManager::deactivateLicense()
{ {
mLicenseKey.clear(); mLicenseKey.clear();
mLicenseExpiry = QDateTime(); mLicenseExpiry = QDateTime();
mGracePeriodStart = QDateTime();
mState = LicenseNone; mState = LicenseNone;
saveState(); saveState();
emit licenseStateChanged(mState); emit licenseStateChanged(mState);

View File

@ -32,6 +32,8 @@ public:
LicenseTrial, LicenseTrial,
LicenseActive, LicenseActive,
LicenseExpired, LicenseExpired,
LicenseGracePeriod,
LicenseBlocked,
LicenseNone, LicenseNone,
LicenseInvalid LicenseInvalid
}; };
@ -46,6 +48,10 @@ public:
QDateTime licenseExpiryDate() const; QDateTime licenseExpiryDate() const;
int trialDaysRemaining() const; int trialDaysRemaining() const;
int trialDurationDays() const; int trialDurationDays() const;
int gracePeriodDaysRemaining() const;
int gracePeriodDurationDays() const;
bool isBlocked() const;
bool isGracePeriod() const;
bool isLicensed() const; bool isLicensed() const;
bool isTrialActive() const; bool isTrialActive() const;
@ -77,7 +83,9 @@ private:
QString mMachineId; QString mMachineId;
QDateTime mTrialStart; QDateTime mTrialStart;
QDateTime mLicenseExpiry; QDateTime mLicenseExpiry;
QDateTime mGracePeriodStart;
QNetworkAccessManager* mNetworkManager; QNetworkAccessManager* mNetworkManager;
static const int GRACE_PERIOD_DAYS = 7;
void loadState(); void loadState();
void saveState(); void saveState();

View File

@ -167,10 +167,10 @@ int main(int argc, char *argv[])
int dialogResult = licenseDialog.exec(); int dialogResult = licenseDialog.exec();
if (dialogResult == QDialog::Rejected) if (dialogResult == QDialog::Rejected)
{ {
// User closed the dialog without activating
licenseState = licenseManager->state(); licenseState = licenseManager->state();
if (licenseState == UBLicenseManager::LicenseNone || if (licenseState == UBLicenseManager::LicenseNone ||
licenseState == UBLicenseManager::LicenseExpired) licenseState == UBLicenseManager::LicenseExpired ||
licenseState == UBLicenseManager::LicenseBlocked)
{ {
qDebug() << "Application exiting: no valid license"; qDebug() << "Application exiting: no valid license";
app.cleanup(); app.cleanup();
@ -178,15 +178,29 @@ int main(int argc, char *argv[])
} }
} }
} }
else if (licenseState == UBLicenseManager::LicenseExpired) else if (licenseState == UBLicenseManager::LicenseExpired ||
licenseState == UBLicenseManager::LicenseGracePeriod)
{ {
UBLicenseDialog licenseDialog; UBLicenseDialog licenseDialog;
licenseDialog.exec(); licenseDialog.exec();
licenseState = licenseManager->state(); licenseState = licenseManager->state();
if (licenseState == UBLicenseManager::LicenseExpired || if (licenseState == UBLicenseManager::LicenseExpired ||
licenseState == UBLicenseManager::LicenseNone) licenseState == UBLicenseManager::LicenseNone ||
licenseState == UBLicenseManager::LicenseBlocked)
{ {
qDebug() << "Application exiting: license expired"; qDebug() << "Application exiting: license expired or blocked";
app.cleanup();
return 0;
}
}
else if (licenseState == UBLicenseManager::LicenseBlocked)
{
UBLicenseDialog licenseDialog;
licenseDialog.exec();
licenseState = licenseManager->state();
if (licenseState == UBLicenseManager::LicenseBlocked)
{
qDebug() << "Application exiting: license blocked, no grace period remaining";
app.cleanup(); app.cleanup();
return 0; return 0;
} }
@ -194,7 +208,7 @@ int main(int argc, char *argv[])
if (licenseState == UBLicenseManager::LicenseTrial) if (licenseState == UBLicenseManager::LicenseTrial)
{ {
qDebug() << "Trial period: " << licenseManager->trialDaysRemaining() qDebug() << "Trial period:" << licenseManager->trialDaysRemaining()
<< "days remaining"; << "days remaining";
} }
else if (licenseState == UBLicenseManager::LicenseActive) else if (licenseState == UBLicenseManager::LicenseActive)
@ -202,6 +216,11 @@ int main(int argc, char *argv[])
qDebug() << "License active, expires:" qDebug() << "License active, expires:"
<< licenseManager->licenseExpiryDate().toString(Qt::ISODate); << licenseManager->licenseExpiryDate().toString(Qt::ISODate);
} }
else if (licenseState == UBLicenseManager::LicenseGracePeriod)
{
qDebug() << "Grace period:" << licenseManager->gracePeriodDaysRemaining()
<< "days remaining. License required.";
}
qDebug() << "file name argument" << fileToOpen; qDebug() << "file name argument" << fileToOpen;
result = app.exec(fileToOpen); result = app.exec(fileToOpen);

View File

@ -7,8 +7,6 @@ target_sources(${PROJECT_NAME} PRIVATE
UBPodcastRecordingPalette.h UBPodcastRecordingPalette.h
intranet/UBIntranetPodcastPublisher.cpp intranet/UBIntranetPodcastPublisher.cpp
intranet/UBIntranetPodcastPublisher.h intranet/UBIntranetPodcastPublisher.h
youtube/UBYouTubePublisher.cpp
youtube/UBYouTubePublisher.h
) )
if(WIN32) if(WIN32)