You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.7 KiB
173 lines
4.7 KiB
#include <iostream>
|
|
|
|
#include <QFile>
|
|
#include <QDebug>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QPixmap>
|
|
#include <QPushButton>
|
|
#include <QLineEdit>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QNetworkAccessManager>
|
|
#include <QNetworkRequest>
|
|
#include <QCryptographicHash>
|
|
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/pem.h>
|
|
|
|
#include "drive_stats.hpp"
|
|
#include "common/params.h"
|
|
#include "common/utilpp.h"
|
|
double MILE_TO_KM = 1.60934;
|
|
|
|
|
|
#if defined(QCOM) || defined(QCOM2)
|
|
const std::string private_key_path = "/persist/comma/id_rsa";
|
|
#else
|
|
const std::string private_key_path = util::getenv_default("HOME", "/.comma/persist/comma/id_rsa", "/persist/comma/id_rsa");
|
|
#endif
|
|
|
|
QByteArray rsa_sign(QByteArray data){
|
|
auto file = QFile(private_key_path.c_str());
|
|
bool r = file.open(QIODevice::ReadOnly);
|
|
assert(r);
|
|
|
|
auto key = file.readAll();
|
|
|
|
BIO *mem = BIO_new_mem_buf(key.data(), key.size());
|
|
assert(mem);
|
|
|
|
RSA *rsa_private = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL);
|
|
assert(rsa_private);
|
|
|
|
auto sig = QByteArray();
|
|
sig.resize(RSA_size(rsa_private));
|
|
|
|
unsigned int sig_len;
|
|
int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private);
|
|
|
|
assert(ret == 1);
|
|
assert(sig_len == sig.size());
|
|
|
|
BIO_free(mem);
|
|
RSA_free(rsa_private);
|
|
|
|
return sig;
|
|
}
|
|
|
|
QString create_jwt(QString dongle_id, int expiry=3600){
|
|
QJsonObject header;
|
|
header.insert("alg", "RS256");
|
|
header.insert("typ", "JWT");
|
|
|
|
auto t = QDateTime::currentSecsSinceEpoch();
|
|
QJsonObject payload;
|
|
payload.insert("identity", dongle_id);
|
|
payload.insert("nbf", t);
|
|
payload.insert("iat", t);
|
|
payload.insert("exp", t + expiry);
|
|
|
|
QString jwt =
|
|
QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64() +
|
|
'.' +
|
|
QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64();
|
|
|
|
auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256);
|
|
auto sig = rsa_sign(hash);
|
|
|
|
jwt += '.' + sig.toBase64();
|
|
|
|
return jwt;
|
|
}
|
|
|
|
QString bold(QString s) {
|
|
return "<b>" + s + "</b>";
|
|
}
|
|
|
|
QWidget *widget(QLayout *l){
|
|
QWidget *q = new QWidget();
|
|
q->setLayout(l);
|
|
return q;
|
|
}
|
|
|
|
QWidget *build_stat(QString name, int stat){
|
|
QVBoxLayout *layout = new QVBoxLayout;
|
|
layout->addWidget(new QLabel(bold(QString("%1").arg(stat))), 1, Qt::AlignCenter);
|
|
layout->addWidget(new QLabel(name),1, Qt::AlignCenter);
|
|
return widget(layout);
|
|
}
|
|
|
|
void DriveStats::replyFinished(QNetworkReply *l){
|
|
QString answer = l->readAll();
|
|
answer.chop(1);
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(answer.toUtf8());
|
|
if (doc.isNull()) {
|
|
qDebug() << "JSON Parse failed";
|
|
}
|
|
QString IsMetric = QString::fromStdString(Params().get("IsMetric"));
|
|
bool metric = (IsMetric =="1");
|
|
|
|
QJsonObject json = doc.object();
|
|
auto all = json["all"].toObject();
|
|
auto week = json["week"].toObject();
|
|
|
|
QGridLayout *gl = new QGridLayout();
|
|
|
|
int all_distance = all["distance"].toDouble()*(metric ? MILE_TO_KM : 1);
|
|
gl->addWidget(new QLabel(bold("ALL TIME")), 0, 0, 1, 3);
|
|
gl->addWidget(build_stat("DRIVES", all["routes"].toDouble()), 1, 0, 3, 1);
|
|
gl->addWidget(build_stat(metric ? "KM" : "MILES", all_distance), 1, 1, 3, 1);
|
|
gl->addWidget(build_stat("HOURS", all["minutes"].toDouble() / 60), 1, 2, 3, 1);
|
|
|
|
QFrame *lineA = new QFrame;
|
|
lineA->setFrameShape(QFrame::HLine);
|
|
lineA->setFrameShadow(QFrame::Sunken);
|
|
lineA->setProperty("class", "line");
|
|
gl->addWidget(lineA, 5, 0, 1, 3);
|
|
|
|
int week_distance = week["distance"].toDouble()*(metric ? MILE_TO_KM : 1);
|
|
gl->addWidget(new QLabel(bold("PAST WEEK")), 6, 0, 1, 3);
|
|
gl->addWidget(build_stat("DRIVES", week["routes"].toDouble()), 7, 0, 3, 1);
|
|
gl->addWidget(build_stat(metric ? "KM" : "MILES", week_distance), 7, 1, 3, 1);
|
|
gl->addWidget(build_stat("HOURS", week["minutes"].toDouble() / 60), 7, 2, 3, 1);
|
|
|
|
|
|
f->setLayout(gl);
|
|
f->setStyleSheet(R"(
|
|
[class="line"]{
|
|
border: 2px solid white;
|
|
}
|
|
[class="outside"]{
|
|
border-radius: 20px;
|
|
border: 2px solid white;
|
|
padding: 10px;
|
|
}
|
|
QLabel{
|
|
font-size: 70px;
|
|
font-weight: 200;
|
|
}
|
|
)");
|
|
|
|
}
|
|
DriveStats::DriveStats(QWidget *parent) : QWidget(parent){
|
|
f = new QFrame;
|
|
f->setProperty("class", "outside");
|
|
QVBoxLayout *v = new QVBoxLayout;
|
|
v->addWidget(f);
|
|
setLayout(v);
|
|
|
|
QString dongle_id = QString::fromStdString(Params().get("DongleId"));
|
|
QString token = create_jwt(dongle_id);
|
|
|
|
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
|
connect(manager, &QNetworkAccessManager::finished, this, &DriveStats::replyFinished);
|
|
|
|
QNetworkRequest request;
|
|
request.setUrl(QUrl("https://api.commadotai.com/v1.1/devices/" + dongle_id + "/stats"));
|
|
request.setRawHeader("Authorization", ("JWT "+token).toUtf8());
|
|
|
|
manager->get(request);
|
|
}
|
|
|