feat(profile): selezione del profilo di cui vedere le sessioni (v0.5.0)
- Impostazioni > Sessions: selettore "Profile" (elenco da /api/profiles; "(server active)" come prima voce = nessun cookie di profilo) - ApiClient: fetchProfiles() + switchProfile(); lo switch usa /api/profile/switch (process_wide=False lato server: il cookie firmato hermes_profile scopa le richieste dell'app per-client; il profilo attivo globale del server non viene toccato); refresh automatico delle sessioni - CookieJar::removeCookiesByName per l'opzione "(server active)" - Il profilo scelto viene riapplicato automaticamente dopo il login - i18n: nuove stringhe tradotte it/fr/de
This commit is contained in:
+78
-1
@@ -1,6 +1,7 @@
|
||||
#include "apiclient.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
@@ -29,6 +30,9 @@ ApiClient::ApiClient(Settings *settings, QObject *parent)
|
||||
m_jar->setFilePath(cfg + QStringLiteral("/cookies.dat"));
|
||||
m_jar->load();
|
||||
m_nam.setCookieJar(m_jar);
|
||||
|
||||
// Il profilo selezionato (impostazione app) e' esposto anche da ApiClient.
|
||||
connect(m_settings, &Settings::profileChanged, this, &ApiClient::profileChanged);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- helpers --
|
||||
@@ -170,7 +174,12 @@ void ApiClient::login(const QString &password)
|
||||
return;
|
||||
}
|
||||
setLoggedIn(true);
|
||||
refreshSessions();
|
||||
// Riapplica il profilo scelto dall'utente: il nuovo cookie di sessione
|
||||
// va rilegato al profilo (switchProfile aggiorna anche le sessioni).
|
||||
if (!m_settings->profile().isEmpty())
|
||||
switchProfile(m_settings->profile());
|
||||
else
|
||||
refreshSessions();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -221,6 +230,74 @@ int ApiClient::sessionsCount() const
|
||||
return m_sessionsModel->rowCount();
|
||||
}
|
||||
|
||||
QString ApiClient::profile() const
|
||||
{
|
||||
return m_settings->profile();
|
||||
}
|
||||
|
||||
void ApiClient::fetchProfiles()
|
||||
{
|
||||
QNetworkReply *reply = m_nam.get(jsonRequest(QStringLiteral("/api/profiles")));
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||
reply->deleteLater();
|
||||
const QByteArray data = reply->readAll();
|
||||
const int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (status < 200 || status >= 300) {
|
||||
qDebug() << "[api] fetchProfiles HTTP" << status;
|
||||
return;
|
||||
}
|
||||
const QJsonArray arr = QJsonDocument::fromJson(data).object()
|
||||
.value(QStringLiteral("profiles")).toArray();
|
||||
QStringList names;
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
const QString n = arr.at(i).toObject().value(QStringLiteral("name")).toString();
|
||||
if (!n.isEmpty())
|
||||
names << n;
|
||||
}
|
||||
qDebug() << "[api] profili disponibili:" << names;
|
||||
if (names != m_profiles) {
|
||||
m_profiles = names;
|
||||
emit profilesChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ApiClient::switchProfile(const QString &name)
|
||||
{
|
||||
const QString target = name.trimmed();
|
||||
if (target.isEmpty()) {
|
||||
// "(attivo sul server)": rimuove il cookie di profilo e torna a
|
||||
// seguire il profilo attivo del server.
|
||||
m_jar->removeCookiesByName(QStringLiteral("hermes_profile"));
|
||||
m_settings->setProfile(QString());
|
||||
emit profileSwitched(QString());
|
||||
refreshSessions();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject body;
|
||||
body.insert(QStringLiteral("name"), target);
|
||||
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/profile/switch")),
|
||||
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply, target]() {
|
||||
reply->deleteLater();
|
||||
const QByteArray data = reply->readAll();
|
||||
const int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (status >= 200 && status < 300) {
|
||||
// Il Set-Cookie firmato (hermes_profile) viene salvato dal jar:
|
||||
// da qui in poi le richieste dell'app sono profilate lato server.
|
||||
m_settings->setProfile(target);
|
||||
qDebug() << "[api] profilo selezionato:" << target;
|
||||
emit profileSwitched(target);
|
||||
refreshSessions();
|
||||
} else {
|
||||
const QString err = errorFromReply(reply, data);
|
||||
qDebug() << "[api] switch profilo fallito:" << err;
|
||||
emit profileSwitchFailed(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ApiClient::openSession(const QString &id)
|
||||
{
|
||||
setLastError(QString());
|
||||
|
||||
@@ -40,6 +40,8 @@ class ApiClient : public QObject
|
||||
Q_PROPERTY(SessionsModel *sessions READ sessions NOTIFY sessionsChanged)
|
||||
Q_PROPERTY(int sessionsCount READ sessionsCount NOTIFY sessionsChanged)
|
||||
Q_PROPERTY(QVariantMap session READ session NOTIFY sessionChanged)
|
||||
Q_PROPERTY(QStringList profiles READ profiles NOTIFY profilesChanged)
|
||||
Q_PROPERTY(QString profile READ profile NOTIFY profileChanged)
|
||||
|
||||
public:
|
||||
explicit ApiClient(Settings *settings, QObject *parent = nullptr);
|
||||
@@ -52,6 +54,8 @@ public:
|
||||
SessionsModel *sessions() const { return m_sessionsModel; }
|
||||
int sessionsCount() const;
|
||||
QVariantMap session() const { return m_session; }
|
||||
QStringList profiles() const { return m_profiles; }
|
||||
QString profile() const;
|
||||
|
||||
// Avvio: verifica lo stato auth (cookie persistito o auth disabilitata).
|
||||
Q_INVOKABLE void checkAuth();
|
||||
@@ -59,6 +63,9 @@ public:
|
||||
Q_INVOKABLE void logout();
|
||||
|
||||
Q_INVOKABLE void refreshSessions();
|
||||
// Profilo server: elenco e selezione (per-client, cookie firmato WebUI).
|
||||
Q_INVOKABLE void fetchProfiles();
|
||||
Q_INVOKABLE void switchProfile(const QString &name);
|
||||
Q_INVOKABLE void openSession(const QString &id);
|
||||
Q_INVOKABLE void newSession();
|
||||
|
||||
@@ -80,6 +87,10 @@ signals:
|
||||
void lastErrorChanged();
|
||||
void sessionsChanged();
|
||||
void sessionChanged();
|
||||
void profilesChanged();
|
||||
void profileChanged();
|
||||
void profileSwitched(const QString &name); // ok (vuoto = attivo server)
|
||||
void profileSwitchFailed(const QString &error);
|
||||
|
||||
void authChecked(bool loggedIn);
|
||||
void sessionOpened(); // openSession/newSession completata
|
||||
@@ -125,6 +136,7 @@ private:
|
||||
SessionsModel *m_sessionsModel;
|
||||
QVariantMap m_session;
|
||||
|
||||
QStringList m_profiles;
|
||||
QString m_streamId;
|
||||
QNetworkReply *m_streamReply;
|
||||
SseParser m_parser;
|
||||
|
||||
@@ -63,6 +63,21 @@ void CookieJar::clear()
|
||||
QFile::remove(m_path);
|
||||
}
|
||||
|
||||
void CookieJar::removeCookiesByName(const QString &name)
|
||||
{
|
||||
const QByteArray raw = name.toUtf8();
|
||||
const QList<QNetworkCookie> cookies = allCookies();
|
||||
bool removed = false;
|
||||
for (int i = 0; i < cookies.size(); ++i) {
|
||||
if (cookies.at(i).name() == raw) {
|
||||
deleteCookie(cookies.at(i)); // protected: accessibile dal subclass
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
save();
|
||||
}
|
||||
|
||||
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
|
||||
{
|
||||
const bool changed = QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
|
||||
|
||||
@@ -21,6 +21,9 @@ public:
|
||||
bool save() const;
|
||||
void clear();
|
||||
|
||||
// Rimuove tutti i cookie con questo nome (es. hermes_profile) e salva.
|
||||
void removeCookiesByName(const QString &name);
|
||||
|
||||
protected:
|
||||
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url) override;
|
||||
|
||||
|
||||
+2
-2
@@ -21,9 +21,9 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
// AppConfigLocation = ~/.config/harbour/hermes (zona persistente della sandbox).
|
||||
app->setOrganizationName(QStringLiteral("harbour"));
|
||||
app->setApplicationName(QStringLiteral("hermes"));
|
||||
app->setApplicationVersion(QStringLiteral("0.4.0"));
|
||||
app->setApplicationVersion(QStringLiteral("0.5.0"));
|
||||
|
||||
qDebug() << "harbour-hermes v0.4.0 build" << __DATE__ << __TIME__;
|
||||
qDebug() << "harbour-hermes v0.5.0 build" << __DATE__ << __TIME__;
|
||||
|
||||
// Traduzioni: it/fr/de da /usr/share/harbour-hermes/translations, in base
|
||||
// alla lingua di sistema (l'inglese e' la lingua sorgente: nessun .qm).
|
||||
|
||||
@@ -123,6 +123,22 @@ void Settings::setVadMaxMs(int ms)
|
||||
emit vadMaxMsChanged();
|
||||
}
|
||||
|
||||
// Profilo server di cui mostrare le sessioni (vuoto = profilo attivo del
|
||||
// server). L'app lo applica per-client tramite il cookie firmato del WebUI.
|
||||
QString Settings::profile() const
|
||||
{
|
||||
return m_settings.value(QStringLiteral("profile"), QString()).toString();
|
||||
}
|
||||
|
||||
void Settings::setProfile(const QString &name)
|
||||
{
|
||||
const QString clean = name.trimmed();
|
||||
if (clean == profile())
|
||||
return;
|
||||
m_settings.setValue(QStringLiteral("profile"), clean);
|
||||
emit profileChanged();
|
||||
}
|
||||
|
||||
QStringList Settings::ttsVoices() const
|
||||
{
|
||||
return m_settings.value(QStringLiteral("ttsVoices")).toStringList();
|
||||
|
||||
@@ -28,6 +28,8 @@ class Settings : public QObject
|
||||
NOTIFY vadThresholdChanged)
|
||||
Q_PROPERTY(int vadMaxMs READ vadMaxMs WRITE setVadMaxMs
|
||||
NOTIFY vadMaxMsChanged)
|
||||
Q_PROPERTY(QString profile READ profile WRITE setProfile
|
||||
NOTIFY profileChanged)
|
||||
Q_PROPERTY(QStringList ttsVoices READ ttsVoices WRITE setTtsVoices
|
||||
NOTIFY ttsVoicesChanged)
|
||||
Q_PROPERTY(QStringList ttsEngines READ ttsEngines WRITE setTtsEngines
|
||||
@@ -62,6 +64,10 @@ public:
|
||||
int vadMaxMs() const;
|
||||
void setVadMaxMs(int ms);
|
||||
|
||||
// Profilo server di cui mostrare le sessioni (vuoto = attivo del server).
|
||||
QString profile() const;
|
||||
void setProfile(const QString &name);
|
||||
|
||||
// Preset per i selettori voce/engine (liste configurabili, default vuote).
|
||||
QStringList ttsVoices() const;
|
||||
void setTtsVoices(const QStringList &list);
|
||||
@@ -81,6 +87,7 @@ signals:
|
||||
void vadSilenceMsChanged();
|
||||
void vadThresholdChanged();
|
||||
void vadMaxMsChanged();
|
||||
void profileChanged();
|
||||
void ttsVoicesChanged();
|
||||
void ttsEnginesChanged();
|
||||
void defaultWorkspaceChanged();
|
||||
|
||||
Reference in New Issue
Block a user