Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion App/App.pro
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ SOURCES += \

OTHER_FILES += \
../conf/api.conf \
migrations/.gitkeep \
migrations/db/* \
migrations/db/.dbdiff.dev_.cfg \
migrations/local/* \
migrations/local/.migrations

Expand Down
8 changes: 4 additions & 4 deletions App/Server/OpenAPIGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ QJsonObject OpenAPIGenerator::retrieveJson(
enuVarComplexity _complexity,
QString _typeName
) {
if (_typeName.startsWith(TAPI_HELEPER_QSP_M2STR_PREFIX))
_typeName = _typeName.mid(sizeof(TAPI_HELEPER_QSP_M2STR_PREFIX), _typeName.size() - static_cast<int>(sizeof(TAPI_HELEPER_QSP_M2STR_PREFIX)) - 1);
if (_typeName.startsWith(NULLABLE_M2STR_PREFIX))
_typeName = _typeName.mid(sizeof(NULLABLE_M2STR_PREFIX), _typeName.size() - static_cast<int>(sizeof(NULLABLE_M2STR_PREFIX)) - 1);

switch (_complexity) {
case COMPLEXITY_Number:
Expand Down Expand Up @@ -152,8 +152,8 @@ QJsonObject OpenAPIGenerator::retrieveJson(
enuVarComplexity _complexity,
QString _typeName
) {
if (_typeName.startsWith(TAPI_HELEPER_QSP_M2STR_PREFIX))
_typeName = _typeName.mid(sizeof(TAPI_HELEPER_QSP_M2STR_PREFIX), _typeName.size() - static_cast<int>(sizeof(TAPI_HELEPER_QSP_M2STR_PREFIX)) - 1);
if (_typeName.startsWith(NULLABLE_M2STR_PREFIX))
_typeName = _typeName.mid(sizeof(NULLABLE_M2STR_PREFIX), _typeName.size() - static_cast<int>(sizeof(NULLABLE_M2STR_PREFIX)) - 1);

switch (_complexity) {
case COMPLEXITY_Number:
Expand Down
83 changes: 52 additions & 31 deletions App/Server/RESTAPIRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* @author Kambiz Zandi <kambizzandi@gmail.com>
*/

#include <algorithm>
#include <QMap>
#include <QMutex>
#include <QTextStream>
Expand All @@ -31,6 +32,8 @@
#include "Interfaces/DBM/clsORMField.h"
#include "Interfaces/Helpers/RESTClientHelper.h"

using namespace std;

namespace Targoman::API {

using namespace TAPI;
Expand Down Expand Up @@ -67,28 +70,28 @@ const QMap<int, intfAPIArgManipulator*> MetaTypeInfoMap = {
QT_FOR_EACH_STATIC_WIDGETS_CLASS(MAKE_INVALID_METATYPE)
};

#define DO_ON_TYPE_NULLABLE_VALID(_complexity, _baseType, _fromVariantLambda, _toORMValueLambda, _descriptionLambda) \
template <> std::function<QVariant(_baseType _value)> tmplAPIArg<_baseType, _complexity, false, true>::toVariantLambda = nullptr; \
template <> std::function<_baseType(QVariant _value, const QByteArray& _paramName)> tmplAPIArg<_baseType, _complexity, false, true>::fromVariantLambda = _fromVariantLambda; \
template <> std::function<QString(const QList<DBM::clsORMField>& _allFields)> tmplAPIArg<_baseType, _complexity, false, true>::descriptionLambda = _descriptionLambda; \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<_baseType, _complexity, false, true>::toORMValueLambda = _toORMValueLambda; \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<_baseType, _complexity, false, true>::fromORMValueLambda = nullptr; \
template <> std::function<QStringList()> tmplAPIArg<_baseType, _complexity, false, true>::optionsLambda = nullptr; \
#define DO_ON_TYPE_NULLABLE_VALID(_complexity, _baseType, _fnFromVariant, _fnToORMValue, _fnDescription) \
template <> std::function<QVariant(_baseType _value)> tmplAPIArg<_baseType, _complexity, false, true>::fnToVariant = nullptr; \
template <> std::function<_baseType(QVariant _value, const QByteArray& _paramName)> tmplAPIArg<_baseType, _complexity, false, true>::fnFromVariant = _fnFromVariant; \
template <> std::function<QString(const QList<DBM::clsORMField>& _allFields)> tmplAPIArg<_baseType, _complexity, false, true>::fnDescription = _fnDescription; \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<_baseType, _complexity, false, true>::fnToORMValue = _fnToORMValue; \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<_baseType, _complexity, false, true>::fnFromORMValue = nullptr; \
template <> std::function<QStringList()> tmplAPIArg<_baseType, _complexity, false, true>::fnOptions = nullptr; \
\
template <> std::function<QVariant(NULLABLE_TYPE(_baseType) _value)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::toVariantLambda = \
template <> std::function<QVariant(NULLABLE_TYPE(_baseType) _value)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fnToVariant = \
[](NULLABLE_TYPE(_baseType) _value) {return NULLABLE_IS_NULL(_value) ? QVariant() : tmplAPIArg<_baseType, _complexity, false, true>::toVariant(*_value);}; \
template <> std::function<NULLABLE_TYPE(_baseType)(QVariant _value, const QByteArray& _paramName)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fromVariantLambda = \
template <> std::function<NULLABLE_TYPE(_baseType)(QVariant _value, const QByteArray& _paramName)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fnFromVariant = \
[](const QVariant& _value, const QByteArray& _paramName) -> NULLABLE_TYPE(_baseType) { \
if (!_value.isValid() || _value.isNull()) return NULLABLE_TYPE(_baseType)(); \
NULLABLE_VAR(_baseType, Value); \
Value = tmplAPIArg<_baseType, _complexity, false, true>::fromVariant(_value, _paramName); \
return Value; \
}; \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::toORMValueLambda = nullptr; \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fromORMValueLambda = nullptr; \
template <> std::function<QStringList()> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::optionsLambda = nullptr; \
template <> std::function<QString(const QList<DBM::clsORMField>& _allFields)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::descriptionLambda = nullptr; \
static tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>* Dangling_QSP_##_baseType = tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::instance(QSP_M2STR(_baseType)); \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fnToORMValue = nullptr; \
template <> std::function<QVariant(const QVariant& _val)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fnFromORMValue = nullptr; \
template <> std::function<QStringList()> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fnOptions = nullptr; \
template <> std::function<QString(const QList<DBM::clsORMField>& _allFields)> tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::fnDescription = nullptr; \
static tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>* Dangling_QSP_##_baseType = tmplAPIArg<NULLABLE_TYPE(_baseType), _complexity, true>::instance(NULLABLE_M2STR(_baseType)); \
//NULLABLE_TYPE(_baseType) Value(new _baseType); -> NULLABLE_VAR(_baseType, Value);


Expand Down Expand Up @@ -401,7 +404,7 @@ QMap<QString, QString> RESTAPIRegistry::extractMethods(
return QString(" = \"%1\"").arg(_apiObject->defaultValue(i).toString().replace("\"", "\\\""));

case COMPLEXITY_Object:
return "null"; ///TODO: update this
return "null"; ///@TODO: update this

case COMPLEXITY_File:
return "";
Expand Down Expand Up @@ -688,10 +691,12 @@ void RESTAPIRegistry::dumpAPIs()
;

if (API.APIObject->ParamTypesName.isEmpty() == false) {
int maxLen = 0;
int maxTypeLen = 0;
int maxNameLen = 0;

for (int ParamsIndex = 0; ParamsIndex < API.APIObject->ParamTypesName.count(); ParamsIndex++) {
maxLen = max(maxLen, API.APIObject->ParamTypesName[ParamsIndex].length());
maxTypeLen = max(maxTypeLen, API.APIObject->ParamTypesName[ParamsIndex].length());
maxNameLen = max(maxNameLen, API.APIObject->ParamNames[ParamsIndex].length());
}

for (int ParamsIndex = 0; ParamsIndex < API.APIObject->ParamTypesName.count(); ParamsIndex++) {
Expand All @@ -718,20 +723,34 @@ void RESTAPIRegistry::dumpAPIs()

bool IsLastParam = (ParamsIndex == API.APIObject->ParamTypesName.count()-1);

TargomanDebug(5).noLabel().noquote().nospace()
<< (IsLastAPI ? " " : "│") << " "
<< (IsLastMethod ? " " : "│") << " "
<< (IsLastParam ? "└" : "├") << "──"
<< " " << QString::number(ParamsIndex + 1) << ": "
<< API.APIObject->ParamTypesName[ParamsIndex].leftJustified(maxLen)
<< " "
<< API.APIObject->ParamNames[ParamsIndex]
<< " = "
<< DefVal
<< " [" << (DefVal.isNull() ? "Null" : "Not Null") << "]"
<< " [" << (DefVal.isValid() ? "Valid" : "Invalid") << "]"
<< (ParamsIndex < API.APIObject->ParamTypesName.count()-1 ? "," : "")
;
if (DefVal.isValid()) {
TargomanDebug(5).noLabel().noquote().nospace()
<< (IsLastAPI ? " " : "│") << " "
<< (IsLastMethod ? " " : "│") << " "
<< (IsLastParam ? "└" : "├") << "──"
<< " " << QString::number(ParamsIndex + 1) << ": "
<< API.APIObject->ParamTypesName[ParamsIndex].leftJustified(maxTypeLen)
<< " "
<< API.APIObject->ParamNames[ParamsIndex].leftJustified(maxNameLen)
<< " = "
<< DefVal
// << " [" << (DefVal.isNull() ? "Null" : "Not Null") << "]"
// << " [" << (DefVal.isValid() ? "Valid" : "Invalid") << "]"
<< (ParamsIndex < API.APIObject->ParamTypesName.count()-1 ? "," : "")
;
} else {
TargomanDebug(5).noLabel().noquote().nospace()
<< (IsLastAPI ? " " : "│") << " "
<< (IsLastMethod ? " " : "│") << " "
<< (IsLastParam ? "└" : "├") << "──"
<< " " << QString::number(ParamsIndex + 1) << ": "
<< API.APIObject->ParamTypesName[ParamsIndex].leftJustified(maxTypeLen)
<< " "
<< API.APIObject->ParamNames[ParamsIndex]
<< (ParamsIndex < API.APIObject->ParamTypesName.count()-1 ? "," : "")
;

}
}
// << " ParamTypesName: [" << API.APIObject->ParamTypesName.count() << "] " << API.APIObject->ParamTypesName.join(", ") << endl
// << " ParamNames: [" << API.APIObject->ParamNames.count() << "] " << API.APIObject->ParamNames.join(", ") << endl
Expand All @@ -744,6 +763,8 @@ void RESTAPIRegistry::dumpAPIs()

}
}

TargomanDebug(5).noLabel().noquote().nospace() << "";
}

/****************************************************/
Expand Down
35 changes: 21 additions & 14 deletions App/Server/RESTServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ void RESTServer::start(fnIsInBlackList_t _fnIPInBlackList) {
}

QObject::connect(&gHTTPServer, &QHttpServer::newConnection, [this](QHttpConnection* _con) {

TargomanLogInfo(7, QString(100, '*'));

if (!this->validateConnection(_con->tcpSocket()->peerAddress(), _con->tcpSocket()->peerPort()))
_con->killConnection();
});
Expand All @@ -103,7 +106,7 @@ void RESTServer::start(fnIsInBlackList_t _fnIPInBlackList) {
QString Path = _req->url().adjusted(QUrl::NormalizePathSegments | QUrl::RemoveAuthority).path(QUrl::PrettyDecoded);

if (Path != _req->url().path())
return RequestHandler->redirect(Path, false);
return RequestHandler->redirect(Path, false);

if (ServerConfigs::PublicPath.value().size() && QFile::exists(ServerConfigs::PublicPath.value() + Path))
return RequestHandler->sendFile(ServerConfigs::PublicPath.value(), Path);
Expand All @@ -117,17 +120,18 @@ void RESTServer::start(fnIsInBlackList_t _fnIPInBlackList) {
if (Path == ServerConfigs::BasePathWithVersion )
return RequestHandler->sendError(qhttp::ESTATUS_NOT_ACCEPTABLE, "No API call provided", {}, true);

TargomanLogInfo(7,
"New API Call ["<<
_req->connection()->tcpSocket()->peerAddress().toString()<<
":"<<
_req->connection()->tcpSocket()->peerPort()<<
"]: "<<
_req->methodString () <<
" "<<
Path<<
"?"<<
_req->url().query());
TargomanLogInfo(7, QString(100, '*'));
TargomanLogInfo(7, "(" << RequestHandler->Index() << ") "
<< "New API Call ["
<< _req->connection()->tcpSocket()->peerAddress().toString()
<< ":"
<< _req->connection()->tcpSocket()->peerPort()
<< "]: "
<< _req->methodString ()
<< " "
<< Path
<< "?"
<< _req->url().query());

//----------------------------------
try {
Expand Down Expand Up @@ -196,10 +200,13 @@ void RESTServer::registerUserDefinedType(const char* _typeName, intfAPIArgManipu
bool RESTServer::validateConnection(const QHostAddress& _peerAddress, quint16 _peerPort) {
enuIPBlackListStatus::Type IPBlackListStatus = enuIPBlackListStatus::Unknown;

if (this->fnIPInBlackList &&
(IPBlackListStatus = this->fnIPInBlackList(_peerAddress)) != enuIPBlackListStatus::Ok) {
if (this->fnIPInBlackList
&& (IPBlackListStatus = this->fnIPInBlackList(_peerAddress)) != enuIPBlackListStatus::Ok) {

TargomanLogWarn(1,"Connection from " + _peerAddress.toString() + " was closed by security provider due to: "+enuIPBlackListStatus::toStr(IPBlackListStatus));

gServerStats.Blocked.inc();

return false;
}

Expand Down
2 changes: 1 addition & 1 deletion App/Server/StaticModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ QVariant IMPL_REST_GET_OR_POST(StaticModule, swaggerui, (
QString File = API.mid(sizeof("/swaggerUI") - 1).replace(QRegularExpression("//+"), "/");

if (File.isEmpty())
return TAPI::ResponseRedirect_t(API + "/").toVariant();
return TAPI::ResponseRedirect_t(API + "/", true).toVariant();

if (File == "/")
File = "index.html";
Expand Down
31 changes: 22 additions & 9 deletions App/Server/clsRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
#include "APICache.hpp"
#include "OpenAPIGenerator.h"

#include "Interfaces/Helpers/URLHelper.h"
using namespace Targoman::API::Helpers;

#include "Interfaces/AAA/Authentication.h"
using namespace Targoman::API::AAA;

Expand All @@ -47,10 +50,14 @@ namespace Targoman::API::Server {
using namespace qhttp::server;
using namespace Targoman::Common;

static quint64 RequestCounter = 1;

clsRequestHandler::clsRequestHandler(QHttpRequest *_req, QHttpResponse *_res, QObject* _parent) :
QObject(_parent),
Request(_req),
Response(_res) {
Response(_res),
RequestIndex(RequestCounter++)
{
this->ElapsedTimer.start();
}

Expand Down Expand Up @@ -133,7 +140,7 @@ void clsRequestHandler::process(const QString& _api) {
else if (JSONObjectIter.value().isObject())
this->Request->addUserDefinedData(JSONObjectIter.key(), QJsonDocument(JSONObjectIter.value().toObject()).toJson());
else if (JSONObjectIter.value().isDouble())
this->Request->addUserDefinedData(JSONObjectIter.key(), QString("%1").arg(JSONObjectIter.value().toDouble()));
this->Request->addUserDefinedData(JSONObjectIter.key(), QString::number(JSONObjectIter.value().toDouble()));
else
this->Request->addUserDefinedData(JSONObjectIter.key(), JSONObjectIter.value().toString());
}
Expand Down Expand Up @@ -295,11 +302,16 @@ void clsRequestHandler::sendTimingsToResponse() {

QStringList Output;

// Output << "app";

for (QMap<QString, quint64>::const_iterator it = this->ServerTimings.constBegin();
it != this->ServerTimings.constEnd();
it++
) {
Output.append(QString("%1;dur=%2").arg(it.key()).arg(ceil(it.value() / 1000.0) / 1000));
QString Label = it.key();
// if (Label.contains(";desc=") == false)
// Label += ";desc=\"total\"";
Output.append(QString("%1;dur=%2").arg(Label).arg(ceil(it.value() / 1000.0) / 1000));
}

this->Response->addHeaderValue("Server-Timing", Output.join(", "));
Expand All @@ -319,15 +331,15 @@ clsRequestHandler::stuResult clsRequestHandler::run(
) {
// QVariantMap ResponseHeaders;

auto FNTiming = [=](const QString &_name, const QString &_desc, quint64 _nanoSecs) {
auto fnTiming = [=](const QString &_name, const QString &_desc, quint64 _nanoSecs) {
this->addToTimings(_name, _desc, _nanoSecs);
};

QScopedPointer<intfAPICallBoom> APICALLBOOM;
if (_apiObject->requiresJWT())
APICALLBOOM.reset(new APICALLBOOM_TYPE_JWT_DECL(FNTiming));
APICALLBOOM.reset(new APICALLBOOM_TYPE_JWT_DECL(fnTiming));
else
APICALLBOOM.reset(new APICALLBOOM_TYPE_NO_JWT_DECL(FNTiming));
APICALLBOOM.reset(new APICALLBOOM_TYPE_NO_JWT_DECL(fnTiming));

try {
for (auto QueryIter = _queries.begin(); QueryIter != _queries.end(); ++QueryIter)
Expand Down Expand Up @@ -357,6 +369,7 @@ clsRequestHandler::stuResult clsRequestHandler::run(
);
} catch (exJWTExpired &exp) {
auto ServerTiming = APICALLBOOM->createScopeTiming("jwt", "renew");

bool IsRenewed = false;
QString NewToken = Authentication::renewExpiredJWT(
JWT,
Expand Down Expand Up @@ -559,7 +572,7 @@ void clsRequestHandler::sendFile(QString _basePath, const QString &_path) {
qint64 FileSize = QFileInfo(*this->FileHandler).size();

this->Response->addHeaderValue("content-type", FileMIME.name());
this->Response->addHeaderValue("content-length", QString("%1").arg(FileSize));
this->Response->addHeaderValue("content-length", QString::number(FileSize));
this->Response->addHeaderValue("Connection", QString("keep-alive"));

this->Response->setStatusCode(qhttp::ESTATUS_OK);
Expand Down Expand Up @@ -651,7 +664,7 @@ void clsRequestHandler::sendResponse(qhttp::TStatusCode _code,

} else if (strcmp(_response.typeName(), "TAPI::ResponseRedirect_t") == 0) {
TAPI::ResponseRedirect_t ResponseRedirect = qvariant_cast<TAPI::ResponseRedirect_t>(_response);
this->redirect(ResponseRedirect.url());
this->redirect(ResponseRedirect.url(), ResponseRedirect.appendBase(), ResponseRedirect.permananet());

} else if (strcmp(_response.typeName(), "TAPI::FileData_t") == 0) {
TAPI::FileData_t FileData = qvariant_cast<TAPI::FileData_t>(_response);
Expand Down Expand Up @@ -695,7 +708,7 @@ void clsRequestHandler::sendCORSOptions() {

void clsRequestHandler::redirect(const QString _path, bool _appendBase, bool _permananet) {
QString Path = _appendBase ? ServerConfigs::BasePathWithVersion + _path : _path;
Path = Path.replace(QRegularExpression("//+"), "/");
Path = URLHelper::normalize(Path);

this->Response->addHeaderValue("Location", Path);

Expand Down
Loading