Add JSON-RPC functions

pull/99/head
larteyoh 1 year ago
parent 40c5c4fe44
commit 3ff1925055

@ -16,6 +16,9 @@ include(CMakeDependentOption)
cmake_dependent_option(NEROSHOP_USE_QT "Build neroshop with Qt" ON "NEROSHOP_BUILD_GUI" OFF)
option(UUID_SYSTEM_GENERATOR "Enable operating system uuid generator" OFF)
option(UUID_TIME_GENERATOR "Enable experimental time-based uuid generator" OFF)
cmake_dependent_option(NEROSHOP_USE_SYSTEM_SOCKETS "Build neroshop with SYSTEM_SOCKETS" ON "NOT NEROSHOP_USE_LIBZMQ;NOT NEROSHOP_USE_LIBUV" OFF)
cmake_dependent_option(NEROSHOP_USE_LIBZMQ "Build neroshop with LibZMQ" ON "NOT NEROSHOP_USE_LIBUV;NOT NEROSHOP_USE_SYSTEM_SOCKETS" OFF)
cmake_dependent_option(NEROSHOP_USE_LIBUV "Build neroshop with LIBUV" ON "NOT NEROSHOP_USE_LIBZMQ;NOT NEROSHOP_USE_SYSTEM_SOCKETS" OFF)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${CMAKE_CURRENT_SOURCE_DIR}/external/monero-cpp/external/monero-project/cmake")
######################################
@ -43,7 +46,7 @@ if(NOT WIN32)
set(Magenta "${Esc}[35m")
set(Cyan "${Esc}[36m")
set(White "${Esc}[37m")
set(BoldRed "${Esc}[1;31m")
set(BoldRed "${Esc}[1;91m")
set(BoldGreen "${Esc}[1;32m")
set(BoldYellow "${Esc}[1;33m")
set(BoldBlue "${Esc}[1;34m")
@ -352,19 +355,6 @@ if(NOT CURL_FOUND)
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -lldap -llber -lnghttp2 -lpsl -lidn2 -lbrotlidec -lzstd -lrtmp") # for Arch (Manjaro)
endif()
######################################
# libuv
find_package(LibUV)
if(LIBUV_FOUND)
message(STATUS "Using LibUV: ${LibUV_LIBRARIES} (v${LibUV_VERSION})")
include_directories(${LibUV_INCLUDE_DIRS})
set(libuv_src ${LibUV_LIBRARIES})
endif()
if(NOT LIBUV_FOUND)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/include ${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/src)
set(libuv_src ${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/.libs/libuv.a) # In case user chooses to build libuv themselves instead of installing it on the system
endif()
######################################
# willemt/raft
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/external/raft/include)
@ -390,7 +380,7 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/build) # target_link_directories on
######################################
# neroshop core source files
set(neroshop_core_src src/core/buyer.cpp src/core/cart.cpp src/core/catalog.cpp src/core/client.cpp src/core/config.cpp src/core/currency_converter.cpp src/core/database.cpp src/core/encryptor.cpp src/core/item.cpp src/core/order.cpp src/core/process.cpp src/core/script.cpp src/core/seller.cpp src/core/server.cpp src/core/user.cpp src/core/validator.cpp src/core/wallet.cpp)
set(neroshop_core_src src/core/buyer.cpp src/core/cart.cpp src/core/catalog.cpp src/core/client.cpp src/core/config.cpp src/core/currency_converter.cpp src/core/database.cpp src/core/encryptor.cpp src/core/item.cpp src/core/order.cpp src/core/process.cpp src/core/script.cpp src/core/seller.cpp src/core/server.cpp src/core/user.cpp src/core/validator.cpp src/core/wallet.cpp src/core/database/sqlite.cpp)
######################################
# neroshop tests
@ -403,9 +393,9 @@ endif()
######################################
# neroshop-daemon
set(daemon_executable "neromon")
add_executable(${daemon_executable} src/daemon/main.cpp src/core/server.cpp)#target_link_libraries(daemon ${curl_src} ${OPENSSL_LIBRARIES}) # curl requires both openssl(used in monero) and zlib(used in dokun-ui)
add_executable(${daemon_executable} src/daemon/main.cpp src/core/server.cpp src/core/database/sqlite.cpp src/core/database.cpp src/core/rpc.cpp)#target_link_libraries(daemon ${curl_src} ${OPENSSL_LIBRARIES}) # curl requires both openssl(used in monero) and zlib(used in dokun-ui)
target_include_directories(${daemon_executable} PRIVATE #[[${CMAKE_CURRENT_SOURCE_DIR}/src/]])
target_link_libraries(${daemon_executable} ${libuv_src} ${raft_src})#set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl") # fixes undefined reference to symbol 'dlsym@@GLIBC_2.2.5' error
target_link_libraries(${daemon_executable} ${sqlite_src} ${raft_src})#set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl") # fixes undefined reference to symbol 'dlsym@@GLIBC_2.2.5' error
######################################
# neroshop-console
@ -414,7 +404,7 @@ if(NEROSHOP_BUILD_CLI)
add_executable(${neroshop_console} src/console/main.cpp ${neroshop_core_src})
target_compile_definitions(${neroshop_console} PRIVATE NEROSHOP_DEBUG)
target_include_directories(${neroshop_console} PRIVATE #[[${CMAKE_CURRENT_SOURCE_DIR}/src/]])
target_link_libraries(${neroshop_console} ${monero_cpp_src} ${sqlite_src} ${qr_code_generator_src} ${raft_src} ${libuv_src} ${curl_src} ${monero_src} ${lua_src} ${linenoise_src})
target_link_libraries(${neroshop_console} ${monero_cpp_src} ${sqlite_src} ${qr_code_generator_src} ${raft_src} ${curl_src} ${monero_src} ${lua_src} ${linenoise_src})
message(STATUS "${BoldMagenta}NEROSHOP_BUILD_CLI option set to ON${ColourReset}")
endif()
@ -427,12 +417,79 @@ if(NEROSHOP_BUILD_GUI)
target_sources(${neroshop_executable} PRIVATE ${neroshop_res} src/gui/main.cpp src/gui/script_controller.cpp src/gui/wallet_controller.cpp src/gui/user_controller.cpp src/gui/image_loader.cpp src/gui/image_provider.cpp src/gui/wallet_qr_provider.cpp src/gui/currency_exchange_rates_provider.cpp src/gui/backend.cpp ${neroshop_core_src})
target_compile_definitions(${neroshop_executable} PRIVATE NEROSHOP_DEBUG)#set_target_properties(${neroshop_executable} PROPERTIES COMPILE_DEFINITIONS "NEROSHOP_DEBUG")
target_include_directories(${neroshop_executable} PRIVATE #[[${CMAKE_CURRENT_SOURCE_DIR}/src/]])
target_link_libraries(${neroshop_executable} ${monero_cpp_src} ${sqlite_src} ${qr_code_generator_src} ${raft_src} ${libuv_src} ${monero_src} ${lua_src})
target_link_libraries(${neroshop_executable} ${monero_cpp_src} ${sqlite_src} ${qr_code_generator_src} ${raft_src} ${monero_src} ${lua_src})
message(STATUS "${BoldMagenta}NEROSHOP_BUILD_GUI option set to ON${ColourReset}")
target_compile_definitions(${neroshop_executable} PRIVATE NEROSHOP_BUILD_GUI)
endif()
######################################
if(NOT NEROSHOP_USE_LIBZMQ AND NOT NEROSHOP_USE_LIBUV)
message(STATUS "${BoldWhite}Using SYSTEM SOCKETS${ColourReset}")
if(NEROSHOP_BUILD_GUI)
target_compile_definitions(${neroshop_executable} PRIVATE NEROSHOP_USE_SYSTEM_SOCKETS)
endif()
if(NEROSHOP_BUILD_CLI)
target_compile_definitions(${neroshop_console} PRIVATE NEROSHOP_USE_SYSTEM_SOCKETS)
endif()
target_compile_definitions(${daemon_executable} PRIVATE NEROSHOP_USE_SYSTEM_SOCKETS)
endif()
######################################
# libuv
find_package(LibUV)
if(LIBUV_FOUND)
if(NEROSHOP_USE_LIBUV)
message(STATUS "${BoldGreen}Using LibUV: ${LibUV_LIBRARIES} (v${LibUV_VERSION})${ColourReset}")
#include_directories(${LibUV_INCLUDE_DIRS})
if(NEROSHOP_BUILD_GUI)
target_compile_definitions(${neroshop_executable} PRIVATE NEROSHOP_USE_LIBUV)
target_link_libraries(${neroshop_executable} ${LibUV_LIBRARIES})
endif()
if(NEROSHOP_BUILD_CLI)
target_compile_definitions(${neroshop_console} PRIVATE NEROSHOP_USE_LIBUV)
target_link_libraries(${neroshop_console} ${LibUV_LIBRARIES})
endif()
target_compile_definitions(${daemon_executable} PRIVATE NEROSHOP_USE_LIBUV)
target_link_libraries(${daemon_executable} ${LibUV_LIBRARIES})
endif()
endif()
if(NOT LIBUV_FOUND)
if(NEROSHOP_USE_LIBUV)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/include ${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/src)
if(NEROSHOP_BUILD_GUI)
target_compile_definitions(${neroshop_executable} PRIVATE NEROSHOP_USE_LIBUV)
target_link_libraries(${neroshop_executable} ${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/.libs/libuv.a)
endif()
if(NEROSHOP_BUILD_CLI)
target_compile_definitions(${neroshop_console} PRIVATE NEROSHOP_USE_LIBUV)
target_link_libraries(${neroshop_console} ${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/.libs/libuv.a)
endif()
target_compile_definitions(${daemon_executable} PRIVATE NEROSHOP_USE_LIBUV)
target_link_libraries(${daemon_executable} ${CMAKE_CURRENT_SOURCE_DIR}/external/libuv/.libs/libuv.a) # In case user chooses to build libuv themselves instead of installing it on the system
endif()
endif()
######################################
# libzmq
find_package(ZeroMQ)
if(ZeroMQ_FOUND)
if(NEROSHOP_USE_LIBZMQ)
message(STATUS "${BoldRed}Using LIBZMQ: ${ZeroMQ_LIBRARY} (v${ZeroMQ_VERSION})${ColourReset}")
if(NEROSHOP_BUILD_GUI)
target_compile_definitions(${neroshop_executable} PRIVATE NEROSHOP_USE_LIBZMQ)
target_link_libraries(${neroshop_executable} ${ZeroMQ_LIBRARIES})
endif()
if(NEROSHOP_BUILD_CLI)
target_compile_definitions(${neroshop_console} PRIVATE NEROSHOP_USE_LIBZMQ)
target_link_libraries(${neroshop_console} ${ZeroMQ_LIBRARIES})
endif()
target_compile_definitions(${daemon_executable} PRIVATE NEROSHOP_USE_LIBZMQ)
target_link_libraries(${daemon_executable} ${ZeroMQ_LIBRARIES})
endif()
endif()
######################################
# libbcrypt
if(DEFINED NEROSHOP_USE_LIBBCRYPT) # -DNEROSHOP_USE_LIBBCRYPT=1
message(STATUS "Using libbcrypt: ${bcrypt_src}")
if(NEROSHOP_BUILD_GUI)
@ -445,6 +502,7 @@ if(DEFINED NEROSHOP_USE_LIBBCRYPT) # -DNEROSHOP_USE_LIBBCRYPT=1
endif()
endif()
######################################
if (UUID_TIME_GENERATOR) # -DUUID_TIME_GENERATOR=1
if(NEROSHOP_BUILD_GUI)
target_compile_definitions(${neroshop_executable} PRIVATE UUID_TIME_GENERATOR)
@ -454,6 +512,7 @@ if (UUID_TIME_GENERATOR) # -DUUID_TIME_GENERATOR=1
endif()
endif()
######################################
if(WIN32)
set(winsock2_src ws2_32.lib) # -lglfw3 -lgdi32 -lopengl32 -limm32
if(NEROSHOP_BUILD_GUI)
@ -465,6 +524,7 @@ if(WIN32)
target_link_libraries(${daemon_executable} ${winsock2_src})
endif()
######################################
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
find_package(LibUUID) # optional
if(UUID_SYSTEM_GENERATOR) # -DUUID_SYSTEM_GENERATOR=1
@ -479,6 +539,8 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_compile_definitions(${neroshop_console} PRIVATE UUID_SYSTEM_GENERATOR) # uuids::uuid_system_generator
target_link_libraries(${neroshop_console} ${LibUUID_LIBRARY})
endif()
target_compile_definitions(${daemon_executable} PRIVATE UUID_SYSTEM_GENERATOR)
target_link_libraries(${daemon_executable} ${LibUUID_LIBRARY})
endif()
endif()
@ -492,6 +554,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_link_libraries(${daemon_executable} ${posix_src})
endif()
######################################
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
find_library(CFLIB CoreFoundation) # optional
if(UUID_SYSTEM_GENERATOR) # -DUUID_SYSTEM_GENERATOR=1
@ -508,6 +571,8 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
endif()
endif()
######################################
# qt5
if(Qt5_FOUND)
if(NEROSHOP_BUILD_GUI AND NEROSHOP_USE_QT)
# Some Linux distros require Qt::Widgets due to the lack of a native platform file dialog implementation

@ -84,7 +84,7 @@ The name _neroshop_ is a combination of the words _nero_, which is Italian for _
| [curl](https://github.com/curl/curl) | ? | currency conversion | :o: |
| [openssl](https://github.com/openssl/openssl) | 1.1.1 | for curl, sha256 sum and message encryption | :heavy_check_mark: |
| [Qt](https://www.qt.io/) | 5.12.8 | graphical user interface | :heavy_check_mark: |
| [libuv](https://github.com/libuv/libuv) | ? | networking and child process | :heavy_check_mark: |
| [libuv](https://github.com/libuv/libuv) | ? | networking and child process | :grey_question: |
| [raft](https://github.com/willemt/raft) | ? | consensus mechanism | :heavy_check_mark: |
| [stduuid](https://github.com/mariusbancila/stduuid) | ? | unique id generation | :o: |
| [linenoise](https://github.com/antirez/linenoise) | ? | command line interface | :heavy_check_mark: :white_square_button: |
@ -216,7 +216,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md)
## License
This project is licensed under the [GNU General Public License (GPLv3)](LICENSE)
This project is licensed under the [GNU General Public License v3.0 (GPLv3)](LICENSE)
## Donations

@ -0,0 +1,139 @@
##=============================================================================
##
## Copyright (c) Kitware, Inc.
## All rights reserved.
## See LICENSE.txt for details.
##
## This software is distributed WITHOUT ANY WARRANTY; without even
## the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
## PURPOSE. See the above copyright notice for more information.
##
##=============================================================================
# - Try to find ZeroMQ headers and libraries
#
# Usage of this module as follows:
#
# find_package(ZeroMQ)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# ZeroMQ_ROOT_DIR Set this variable to the root installation of
# ZeroMQ if the module has problems finding
# the proper installation path.
#
# Variables defined by this module:
#
# ZeroMQ_FOUND System has ZeroMQ libs/headers
# ZeroMQ_LIBRARIES The ZeroMQ libraries
# ZeroMQ_INCLUDE_DIR The location of ZeroMQ headers
# ZeroMQ_VERSION The version of ZeroMQ
find_path(ZeroMQ_ROOT_DIR
NAMES include/zmq.h
)
if(MSVC)
#add in all the names it can have on windows
if(CMAKE_GENERATOR_TOOLSET MATCHES "v140" OR MSVC14)
set(_zmq_TOOLSET "-v140")
elseif(CMAKE_GENERATOR_TOOLSET MATCHES "v120" OR MSVC12)
set(_zmq_TOOLSET "-v120")
elseif(CMAKE_GENERATOR_TOOLSET MATCHES "v110_xp")
set(_zmq_TOOLSET "-v110_xp")
elseif(CMAKE_GENERATOR_TOOLSET MATCHES "v110" OR MSVC11)
set(_zmq_TOOLSET "-v110")
elseif(CMAKE_GENERATOR_TOOLSET MATCHES "v100" OR MSVC10)
set(_zmq_TOOLSET "-v100")
elseif(CMAKE_GENERATOR_TOOLSET MATCHES "v90" OR MSVC90)
set(_zmq_TOOLSET "-v90")
endif()
set(_zmq_versions
"4_1_5" "4_1_4" "4_1_3" "4_1_2" "4_1_1" "4_1_0"
"4_0_8" "4_0_7" "4_0_6" "4_0_5" "4_0_4" "4_0_3" "4_0_2" "4_0_1" "4_0_0"
"3_2_5" "3_2_4" "3_2_3" "3_2_2" "3_2_1" "3_2_0" "3_1_0")
set(_zmq_release_names)
set(_zmq_debug_names)
foreach( ver ${_zmq_versions})
list(APPEND _zmq_release_names "libzmq${_zmq_TOOLSET}-mt-${ver}")
endforeach()
foreach( ver ${_zmq_versions})
list(APPEND _zmq_debug_names "libzmq${_zmq_TOOLSET}-mt-gd-${ver}")
endforeach()
#now try to find the release and debug version
find_library(ZeroMQ_LIBRARY_RELEASE
NAMES ${_zmq_release_names} zmq libzmq
HINTS ${ZeroMQ_ROOT_DIR}/bin
${ZeroMQ_ROOT_DIR}/lib
)
find_library(ZeroMQ_LIBRARY_DEBUG
NAMES ${_zmq_debug_names} zmq libzmq
HINTS ${ZeroMQ_ROOT_DIR}/bin
${ZeroMQ_ROOT_DIR}/lib
)
if(ZeroMQ_LIBRARY_RELEASE AND ZeroMQ_LIBRARY_DEBUG)
set(ZeroMQ_LIBRARY
debug ${ZeroMQ_LIBRARY_DEBUG}
optimized ${ZeroMQ_LIBRARY_RELEASE}
)
elseif(ZeroMQ_LIBRARY_RELEASE)
set(ZeroMQ_LIBRARY ${ZeroMQ_LIBRARY_RELEASE})
elseif(ZeroMQ_LIBRARY_DEBUG)
set(ZeroMQ_LIBRARY ${ZeroMQ_LIBRARY_DEBUG})
endif()
else()
find_library(ZeroMQ_LIBRARY
NAMES zmq libzmq
HINTS ${ZeroMQ_ROOT_DIR}/lib
)
endif()
find_path(ZeroMQ_INCLUDE_DIR
NAMES zmq.h
HINTS ${ZeroMQ_ROOT_DIR}/include
)
function(extract_version_value value_name file_name value)
file(STRINGS ${file_name} val REGEX "${value_name} .")
string(FIND ${val} " " last REVERSE)
string(SUBSTRING ${val} ${last} -1 val)
string(STRIP ${val} val)
set(${value} ${val} PARENT_SCOPE)
endfunction(extract_version_value)
extract_version_value("ZMQ_VERSION_MAJOR" ${ZeroMQ_INCLUDE_DIR}/zmq.h MAJOR)
extract_version_value("ZMQ_VERSION_MINOR" ${ZeroMQ_INCLUDE_DIR}/zmq.h MINOR)
extract_version_value("ZMQ_VERSION_PATCH" ${ZeroMQ_INCLUDE_DIR}/zmq.h PATCH)
set(ZeroMQ_VER "${MAJOR}.${MINOR}.${PATCH}")
#We are using the 2.8.10 signature of find_package_handle_standard_args,
#as that is the version that ParaView 5.1 && VTK 6/7 ship, and inject
#into the CMake module path. This allows our FindModule to work with
#projects that include VTK/ParaView before searching for Remus
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
ZeroMQ
REQUIRED_VARS ZeroMQ_LIBRARY ZeroMQ_INCLUDE_DIR
VERSION_VAR ZeroMQ_VER
)
set(ZeroMQ_FOUND ${ZEROMQ_FOUND})
set(ZeroMQ_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIR})
set(ZeroMQ_LIBRARIES ${ZeroMQ_LIBRARY})
set(ZeroMQ_VERSION ${ZeroMQ_VER})
mark_as_advanced(
ZeroMQ_ROOT_DIR
ZeroMQ_LIBRARY
ZeroMQ_LIBRARY_DEBUG
ZeroMQ_LIBRARY_RELEASE
ZeroMQ_INCLUDE_DIR
ZeroMQ_VERSION
)

@ -36,6 +36,7 @@ Object {
property string angleLeft: "\uf104"
property string angleRight: "\uf105"
property string cog: "\uf013"
property string coin: "\uf85c"
property string coins: "\uf51e"
property string lock: "\uf023"
property string lockOpen: "\uf3c1"
@ -55,6 +56,38 @@ Object {
property string cloud: "\uf0c2"
property string house: "\uf015"
property string ellipsis: "\uf141"
property string cartShopping: "\uf07a"
property string shop: "\uf54f"
property string store: "\uf54e"
property string tag: "\uf02b"
property string tags: "\uf02c"
property string truck: "\uf0d1"
property string dolly: "\uf472"
property string personDolly: "\uf4d0" // use this icon for item pickup option
property string paperPlane: "\uf1d8"
property string ship: "\uf21a"
property string shield: "\uf132"
property string shieldHalf: "\uf3ed"
property string bolt: "\uf0e7"
property string trash: "\uf1f8"
property string inbox: "\uf01c"
property string volume: "\uf6a8"
property string barcode: "\uf02a"
property string barcodeRead: "\uf464"
//property string plus: "\u2b"
property string pen: "\uf304"
property string penToSquare: "\uf044"
property string droplet: "\uf043"
property string fire: "\uf06d"
property string globe: "\uf0ac"
property string bug: "\uf188"
property string database: "\uf1c0"
property string doorOpen: "\uf52b"
property string mailbox: "\uf813"
property string wallet: "\uf555"
property string desktop: "\uf390"
property string robot: "\uf544"
property string handshake: "\uf2b5"
////property string terminal: ""
////property string upload: "\u"
////property string ?: "\u"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1013 B

After

Width:  |  Height:  |  Size: 744 B

@ -39,7 +39,7 @@ GridView {
visible: true
width: catalogGrid.cellWidth-catalogGrid.spacing
height: catalogGrid.cellHeight-catalogGrid.spacing
color: (NeroshopComponents.Style.darkTheme) ? (NeroshopComponents.Style.themeName == "PurpleDust" ? "#17171c" : "#1d1d1d") : "#c9c9cd"//"#e6e6e6"//"#f0f0f0"
color: (NeroshopComponents.Style.darkTheme) ? (NeroshopComponents.Style.themeName == "PurpleDust" ? "#17171c" : "#181a1b") : "#c9c9cd"//"#e6e6e6"//"#f0f0f0"
border.color: (NeroshopComponents.Style.darkTheme) ? "#404040" : "#4d4d4d"////(NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
border.width: 0
radius: 5

@ -23,7 +23,7 @@ ListView {
delegate: Rectangle {
id: productBox
width: catalogList.boxWidth; height: catalogList.boxHeight // The height of each individual model item/ list element
color: (NeroshopComponents.Style.darkTheme) ? (NeroshopComponents.Style.themeName == "PurpleDust" ? "#17171c" : "#1d1d1d") : "#c9c9cd"//"#f0f0f0"
color: (NeroshopComponents.Style.darkTheme) ? (NeroshopComponents.Style.themeName == "PurpleDust" ? "#17171c" : "#181a1b") : "#c9c9cd"//"#f0f0f0"
border.color: (NeroshopComponents.Style.darkTheme) ? "#404040" : "#4d4d4d"////(NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
border.width: 0
radius: 5

@ -2,7 +2,8 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
//import FontAwesome 1.0
import FontAwesome 1.0
import "." as NeroshopComponents
Item {
@ -60,20 +61,9 @@ Item {
Layout.fillHeight: true
ScrollBar.vertical: ScrollBar { }
model: {
// Get monero nodes from settings.lua
/*let network_type = Wallet.getNetworkTypeString()
return Script.getTableStrings("neroshop.monero.nodes." + network_type)*/
// Get monero nodes from https://monero.fail/health.json
////return Backend.getMoneroNodeList()
// Get only stagenet nodes (for now)
let stagenet_nodes = []
const monero_node_list = Backend.getMoneroNodeList()
for(let i = 0; i < monero_node_list.length; i++) {
if(monero_node_list[i].address.includes("38081") || monero_node_list[i].address.includes("38089")) {
stagenet_nodes.push(monero_node_list[i])
}
}
return stagenet_nodes;
// Get all monero nodes from https://monero.fail/health.json
const monero_node_list = Backend.getNodeList("monero")
return monero_node_list
}
delegate: Item {
width: listView.width
@ -100,16 +90,18 @@ Item {
Label {
id: nodeStatusLabel
text: status ? "✅" : "❌"
color: status ? "#698b22" : "#dd4b4b"
text: (typeof modelData === "string") ? qsTr("\uf1ce") : (status ? qsTr("\uf14a") : qsTr("\uf00d"))//"" : ""
color: (typeof modelData === "string") ? "royalblue" : (status ? "#698b22" : "#dd4b4b")
font.bold: true
font.family: FontAwesome.fontFamily
Layout.maximumWidth: 25
property bool status: modelData.available
property bool status: (typeof modelData === "string") ? false : modelData.available
}
Label {
id: nodeAddressLabel
Layout.fillWidth: true
text: modelData.address//"node.neroshop.org:38081"//:18081"
text: (typeof modelData === "string") ? modelData : modelData.address//"node.neroshop.org:38081"//:18081"
color: delegateRow.parent.ListView.isCurrentItem ? "#471d00" : ((NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000")
font.bold: delegateRow.parent.ListView.isCurrentItem ? true : false
elide: Label.ElideRight
@ -119,7 +111,7 @@ Item {
id: nodeHeightLabel
Layout.minimumWidth: heightTitle.width//50
Layout.maximumWidth: heightTitle.width//50
text: modelData.last_height
text: (typeof modelData === "string") ? "- -" : modelData.last_height
color: nodeAddressLabel.color
font.bold: nodeAddressLabel.font.bold
elide: Label.ElideRight

@ -18,6 +18,7 @@ Row {//RowLayout {
property real buttonRadius: 5
property real radius: buttonRadius
property bool showDirectionalIcons: false
width: childrenRect.width; height: childrenRect.height
Button {
id: backButton
@ -35,6 +36,11 @@ Row {//RowLayout {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !backButton.disabled ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
TextField {
@ -71,6 +77,11 @@ Row {//RowLayout {
verticalAlignment: Text.AlignVCenter
//font.family: "Font Awesome 6 Free"//FontAwesome.fontFamily//FontAwesome.fontFamilySolid
//font.weight: Font.Bold
}
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !forwardButton.disabled ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}

@ -22,6 +22,7 @@ Popup {
// General tab properties
property alias theme: themeBox
property alias currency: currencyBox
property alias hideHomepageButton: hideHomepageButtonSwitch.checked
// Wallet settings
property alias balanceDisplay: balanceDisplayBox.currentIndex//property alias balanceDisplay: balanceDisplayBox.currentText
property alias balanceAmountPrecision: balancePrecisionBox.currentText
@ -151,7 +152,12 @@ Popup {
verticalAlignment: Text.AlignVCenter
font.bold: true//(parent.checked) ? true : false
//font.family: FontAwesome.fontFamily
}
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !parent.checked ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
TabButton {
@ -176,6 +182,11 @@ Popup {
font.bold: true
//font.family: FontAwesome.fontFamily
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !parent.checked ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}
/*Rectangle {
@ -255,33 +266,33 @@ Popup {
//Layout.alignment: Qt.AlignLeft; Layout.leftMargin: 0
}
NeroshopComponents.ComboBox {
id: currencyBox
anchors.right: parent.right//Layout.alignment: Qt.AlignRight; Layout.rightMargin: 0
width: settingsStack.comboBoxWidth
currentIndex: model.indexOf(Script.getString("neroshop.generalsettings.currency").toUpperCase())
displayText: currentText
////property string lastCurrencySet: (Script.getString("neroshop.generalsettings.currency")) ? Script.getString("neroshop.generalsettings.currency") : "USD"
//editable: true; selectTextByMouse: true
model: Backend.getCurrencyList()
//implicitContentWidthPolicy: ComboBox.WidestText//ComboBox.ContentItemImplicitWidth
onAccepted: {
if (find(editText) === -1)
model.append({text: editText})
}
NeroshopComponents.ComboBox {
id: currencyBox
anchors.right: parent.right//Layout.alignment: Qt.AlignRight; Layout.rightMargin: 0
width: settingsStack.comboBoxWidth
currentIndex: model.indexOf(Script.getString("neroshop.generalsettings.currency").toUpperCase())
displayText: currentText
////property string lastCurrencySet: (Script.getString("neroshop.generalsettings.currency")) ? Script.getString("neroshop.generalsettings.currency") : "USD"
//editable: true; selectTextByMouse: true
model: Backend.getCurrencyList()
//implicitContentWidthPolicy: ComboBox.WidestText//ComboBox.ContentItemImplicitWidth
onAccepted: {
if (find(editText) === -1)
model.append({text: editText})
}
onActivated: {
displayText = currentText
priceDisplayText.currency = displayText
////lastCurrencySet = currentText
onActivated: {
displayText = currentText
priceDisplayText.currency = displayText
////lastCurrencySet = currentText
}
indicatorWidth: settingsStack.comboBoxButtonWidth
indicatorDoNotPassBorder: settingsStack.comboBoxNestedButton
color: "#f2f2f2"//(NeroshopComponents.Style.darkTheme) ? "#101010" : "#f0f0f0"
//textColor: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
}
indicatorWidth: settingsStack.comboBoxButtonWidth
indicatorDoNotPassBorder: settingsStack.comboBoxNestedButton
color: "#f2f2f2"//(NeroshopComponents.Style.darkTheme) ? "#101010" : "#f0f0f0"
//textColor: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
}
}
// Price API
// Price API - TODO
/*Item {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
@ -295,7 +306,7 @@ Popup {
id: priceApiBox
anchors.right: parent.right
width: settingsStack.comboBoxWidth; indicatorWidth: settingsStack.comboBoxButtonWidth
model: ["CoinGecko", "CoinMarketCap"]
//model: ["CoinGecko", "CoinMarketCap"]
Component.onCompleted: currentIndex = find("CoinGecko")
indicatorDoNotPassBorder: settingsStack.comboBoxNestedButton
color: "#f2f2f2"
@ -327,55 +338,73 @@ Popup {
}
ColumnLayout {
id: themeColumn
id: appColumn
width: parent.width; height: childrenRect.height
//spacing: 200 // spacing between Row items
Item {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Text {
text: qsTr("Theme:")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.verticalCenter: themeBox.verticalCenter//Layout.alignment: Qt.AlignLeft; Layout.leftMargin: 0
}
NeroshopComponents.ComboBox {
id: themeBox
anchors.right: parent.right//Layout.alignment: Qt.AlignRight; Layout.rightMargin: 0
width: settingsStack.comboBoxWidth
currentIndex: model.indexOf(NeroshopComponents.Style.themeName)//Component.onCompleted: currentIndex = model.indexOf(NeroshopComponents.Style.themeName) // Set the initial currentIndex to the index in the array containing themeName string
displayText: currentText
property string lastUsedDarkTheme: (Script.getBoolean("neroshop.generalsettings.application.theme.dark")) ? Script.getString("neroshop.generalsettings.application.theme.name") : "DefaultDark"
property string lastUsedLightTheme: (!Script.getBoolean("neroshop.generalsettings.application.theme.dark")) ? Script.getString("neroshop.generalsettings.application.theme.name") : "DefaultLight"
model: ["DefaultDark", "DefaultLight", "PurpleDust"]
onActivated: {
if(currentText == "PurpleDust") {
NeroshopComponents.Style.darkTheme = true
lastUsedDarkTheme = currentText
}
if(currentText == "DefaultDark") {
NeroshopComponents.Style.darkTheme = true
lastUsedDarkTheme = currentText
}
if(currentText == "DefaultLight") {
NeroshopComponents.Style.darkTheme = false
lastUsedLightTheme = currentText
}
displayText = currentText
NeroshopComponents.Style.themeName = displayText // update the actual theme (name)
themeSwitcher.checked = !NeroshopComponents.Style.darkTheme // update the theme switch
// NOTE: on app launch, the theme will ALWAYS be reset back to its default unless you change the theme settings in your configuration file
//todo: change theme in configuration file too
console.log("Theme set to", currentText)
Text {
text: qsTr("Theme:")
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
anchors.verticalCenter: themeBox.verticalCenter//Layout.alignment: Qt.AlignLeft; Layout.leftMargin: 0
}
indicatorWidth: settingsStack.comboBoxButtonWidth
indicatorDoNotPassBorder: settingsStack.comboBoxNestedButton
color: "#f2f2f2"
} // ComboBox
NeroshopComponents.ComboBox {
id: themeBox
anchors.right: parent.right//Layout.alignment: Qt.AlignRight; Layout.rightMargin: 0
width: settingsStack.comboBoxWidth
currentIndex: model.indexOf(NeroshopComponents.Style.themeName)//Component.onCompleted: currentIndex = model.indexOf(NeroshopComponents.Style.themeName) // Set the initial currentIndex to the index in the array containing themeName string
displayText: currentText
property string lastUsedDarkTheme: (Script.getBoolean("neroshop.generalsettings.application.theme.dark")) ? Script.getString("neroshop.generalsettings.application.theme.name") : "DefaultDark"
property string lastUsedLightTheme: (!Script.getBoolean("neroshop.generalsettings.application.theme.dark")) ? Script.getString("neroshop.generalsettings.application.theme.name") : "DefaultLight"
model: ["DefaultDark", "DefaultLight", "PurpleDust"]
onActivated: {
if(currentText == "PurpleDust") {
NeroshopComponents.Style.darkTheme = true
lastUsedDarkTheme = currentText
}
if(currentText == "DefaultDark") {
NeroshopComponents.Style.darkTheme = true
lastUsedDarkTheme = currentText
}
if(currentText == "DefaultLight") {
NeroshopComponents.Style.darkTheme = false
lastUsedLightTheme = currentText
}
displayText = currentText
NeroshopComponents.Style.themeName = displayText // update the actual theme (name)
themeSwitcher.checked = !NeroshopComponents.Style.darkTheme // update the theme switch
// NOTE: on app launch, the theme will ALWAYS be reset back to its default unless you change the theme settings in your configuration file
//todo: change theme in configuration file too
console.log("Theme set to", currentText)
}
indicatorWidth: settingsStack.comboBoxButtonWidth
indicatorDoNotPassBorder: settingsStack.comboBoxNestedButton
color: "#f2f2f2"
} // ComboBox
}
// Window
// Hide homepage button
Item {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Text {
anchors.verticalCenter: hideHomepageButtonSwitch.verticalCenter
text: qsTr("Hide homepage button:")
color: NeroshopComponents.Style.darkTheme ? "#ffffff" : "#000000"
}
NeroshopComponents.Switch {
id: hideHomepageButtonSwitch
anchors.right: parent.right; anchors.rightMargin: 5
//width: settingsStack.comboBoxWidth
checked: true
radius: 13
backgroundCheckedColor: "#605185"
}
}
} // RowLayout2
} // GroupBox2
GroupBox {
GroupBox {
//Layout.row: 2
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: settingsStack.contentBoxWidth
@ -500,7 +529,7 @@ Popup {
NeroshopComponents.Switch {
id: showCurrencySignSwitch
anchors.right: parent.right; anchors.rightMargin: 5
width: settingsStack.comboBoxWidth
//width: settingsStack.comboBoxWidth
checked: false
radius: 13
backgroundCheckedColor: "#605185"
@ -570,7 +599,7 @@ Popup {
NeroshopComponents.Switch {
id: hideProductDetailsSwitch
anchors.right: parent.right; anchors.rightMargin: 5
width: settingsStack.comboBoxWidth
//width: settingsStack.comboBoxWidth
checked: false
radius: 13
backgroundCheckedColor: "#605185"
@ -610,7 +639,7 @@ Popup {
NeroshopComponents.Switch {
id: gridDetailsAlignCenterSwitch
anchors.right: parent.right; anchors.rightMargin: 5
width: settingsStack.comboBoxWidth
//width: settingsStack.comboBoxWidth
checked: false
radius: 13
backgroundCheckedColor: "#605185"

@ -37,7 +37,7 @@ QtObject {
tertiaryColor = "#393947"//"#4f4f63"//<= tint
}
else { //"DefaultDark"
primaryColor = "#1a1a1a"//"#202020"////"#141414"
primaryColor = "#131415"//"#1a1a1a"//"#202020"////"#141414"
secondaryColor = "#202020"//"#2e2e2e"////"#1a1a1a"
tertiaryColor = "#595959"
}

@ -11,56 +11,55 @@ Item {
property int currentIndex: viewButtonGroup.checkedButton.buttonIndex
width: childrenRect.width; height: childrenRect.height
ButtonGroup {
id: viewButtonGroup
buttons: column.children
exclusive: true // only one button in the group can be checked at any given time
onClicked: {
console.log("Switched to", button.text)
button.checked = true
ButtonGroup {
id: viewButtonGroup
buttons: column.children
exclusive: true // only one button in the group can be checked at any given time
onClicked: {
console.log("Switched to", button.text)
button.checked = true
}
}
}
Row {
id: column
spacing: 2
Button {
checked: true
text: qsTr("Grid view")
ButtonGroup.group: viewButtonGroup // attaches a button to a button group
property int buttonIndex: 0
display: AbstractButton.IconOnly
icon.source: "qrc:/images/grid.png"
icon.color: !this.checked ? "#39304f" : "#ffffff"// icon color is set automatically unless we set it ourselves, which we do here
background: Rectangle {
radius: viewToggle.radius
color: parent.checked ? "#39304f" : "#e0e0e0"//"#353637" : "#e0e0e0"// rgb(53, 54, 55), rgb(224, 224, 224);
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !parent.checked ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
Row {
id: column
spacing: 2
Button {
text: qsTr("List view")
ButtonGroup.group: viewButtonGroup
property int buttonIndex: 1
display: AbstractButton.IconOnly
icon.source: "qrc:/images/list.png"
icon.color: !this.checked ? "#39304f" : "#ffffff"
background: Rectangle {
radius: viewToggle.radius
color: parent.checked ? "#39304f" : "#e0e0e0"//"#353637" : "#e0e0e0"
Button {
checked: true
text: qsTr("Grid view")
ButtonGroup.group: viewButtonGroup // attaches a button to a button group
property int buttonIndex: 0
display: AbstractButton.IconOnly
icon.source: "qrc:/images/grid.png"
icon.color: !this.checked ? "#39304f" : "#ffffff"// icon color is set automatically unless we set it ourselves, which we do here
background: Rectangle {
radius: viewToggle.radius
color: parent.checked ? "#39304f" : "#e0e0e0"//"#353637" : "#e0e0e0"// rgb(53, 54, 55), rgb(224, 224, 224);
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !parent.checked ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !parent.checked ? Qt.PointingHandCursor : Qt.ArrowCursor
Button {
text: qsTr("List view")
ButtonGroup.group: viewButtonGroup
property int buttonIndex: 1
display: AbstractButton.IconOnly
icon.source: "qrc:/images/list.png"
icon.color: !this.checked ? "#39304f" : "#ffffff"
background: Rectangle {
radius: viewToggle.radius
color: parent.checked ? "#39304f" : "#e0e0e0"//"#353637" : "#e0e0e0"
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
cursorShape: !parent.checked ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}
}
}

@ -32,7 +32,7 @@ ApplicationWindow {
Button {//Image {
id: neroshopLogoImageButton
visible: true
visible: !settingsDialog.hideHomepageButton
property real iconSize: 30
icon.source: (NeroshopComponents.Style.darkTheme) ? "qrc:/images/appicons/Vector_Illustrator Files/LogoLight.svg" : "qrc:/images/appicons/Vector_Illustrator Files/LogoDark.svg"
icon.color: icon.color

@ -153,6 +153,7 @@ Page {
Layout.preferredWidth: childrenRect.width
Layout.preferredHeight: childrenRect.height
Layout.maximumWidth: scrollView.width
visible: itemsRepeater.count > 0
Column {
spacing: 10

@ -6,8 +6,9 @@ using namespace neroshop;
// linenoise
#include <linenoise.h>
int main() {
int main(int argc, char** argv) {
// todo: bind command names to functions
//-------------------------
if(!neroshop::create_config()) {
neroshop::load_config();
}
@ -26,7 +27,6 @@ int main() {
std::cout << "failed to get nodes in the config file\nCheck your config file in ~/.config/neroshop" << std::endl;
}
//-------------------------
// todo: bind command names to functions
char * line = NULL;
while((line = linenoise("neroshop-console> ")) != NULL) {
// Do something with the string
@ -93,10 +93,10 @@ int main() {
}*/
//-------------------------
//neroshop::Server server;
//server.bind("exit", [](void) { ::system("exit"); });
/*neroshop::Server server;
server.bind("exit", [](void) { ::system("exit"); });*/
//client.call("exit" /*args ...*/);
//neroshop::Client client;client.call(server, "exit" /*args ...*/);
//-------------------------
return 0;
}

@ -18,7 +18,7 @@ neroshop::Cart::~Cart() {
// normal
////////////////////
void neroshop::Cart::load(const std::string& user_id) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Set the cart's id
this->id = database->get_text_params("SELECT uuid FROM cart WHERE user_id = $1", { user_id });
@ -81,7 +81,7 @@ void neroshop::Cart::load(const std::string& user_id) {
}
////////////////////
void neroshop::Cart::add(const std::string& user_id, const std::string& product_id, int quantity) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
if(quantity < 1) return;
std::string item_name = database->get_text_params("SELECT name FROM products WHERE uuid = $1", { product_id }); // temporary
@ -134,7 +134,7 @@ void neroshop::Cart::add(const std::string& user_id, const neroshop::Item& item,
}
////////////////////
void neroshop::Cart::remove(const std::string& user_id, const std::string& product_id, int quantity) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
}
@ -144,7 +144,7 @@ void neroshop::Cart::remove(const std::string& user_id, const neroshop::Item& it
}
////////////////////
void neroshop::Cart::empty(const std::string& user_id) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
std::string cart_id = database->get_text_params("SELECT uuid FROM cart WHERE user_id = $1", { user_id });
database->execute_params("DELETE FROM cart_item WHERE cart_id = $1;", { cart_id });

@ -9,7 +9,7 @@ neroshop::Client::~Client() {
}
////////////////////
void neroshop::Client::create() {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
if(socket) return; // socket must be null before a new one can be created (if socket is not null then it means it was never closed)
socket = ::socket(AF_INET, SOCK_STREAM, 0);
if(socket < 0) {
@ -19,7 +19,7 @@ void neroshop::Client::create() {
}
////////////////////
bool neroshop::Client::connect(unsigned int port, std::string address) {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
struct hostent * host = gethostbyname(address.c_str());
if(host == nullptr) {
std::cerr << "No host to connect to" << std::endl;
@ -42,7 +42,7 @@ bool neroshop::Client::connect(unsigned int port, std::string address) {
}
////////////////////
void neroshop::Client::write(const std::string& text) {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
ssize_t write_result = ::write(socket, text.c_str(), text.length());
if(write_result < 0) { // -1 = error
std::cerr << "Could not write to server" << std::endl;
@ -52,7 +52,7 @@ void neroshop::Client::write(const std::string& text) {
////////////////////
std::string neroshop::Client::read()
{
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
memset(buffer, 0, 256); // clear buffer (fills buffer with 0's) before reading into buffer//bzero(buffer, 256); // bzero is deprecated
ssize_t read_result = ::read(socket, buffer, 255);
if(read_result < 0) {
@ -60,10 +60,11 @@ std::string neroshop::Client::read()
}
return static_cast<std::string>(buffer);
#endif
return "";
}
////////////////////
void neroshop::Client::close() {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
if(socket == 0) return;
::close(socket);
socket = 0;
@ -71,7 +72,7 @@ void neroshop::Client::close() {
}
////////////////////
void neroshop::Client::shutdown() {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
::shutdown(socket, SHUT_RDWR); // SHUT_RD, SHUT_WR, SHUT_RDWR
#endif
}

@ -1,18 +1,27 @@
#ifndef CLIENT_HPP_NEROSHOP
#define CLIENT_HPP_NEROSHOP
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#endif
#if defined(NEROSHOP_USE_LIBZMQ)
#include <zmq.h>
#endif
#if defined(NEROSHOP_USE_LIBUV)
#include <uv.h>
//#include <raft.h>
#include <memory> // std::unique_ptr
#include <cstring> // memset
#if defined(__gnu_linux__)
#include <unistd.h> // ::close
#endif
#include "debug.hpp"
#include "server.hpp" // not sure if server should even be included in client ┐(´•_•`)┌
#include <memory> // std::unique_ptr
#include <cstring> // memset
#include "debug.hpp"
#include "server.hpp" // temporary
namespace neroshop {
class Client {
@ -38,26 +47,23 @@ public:
//! \param name The name of the functor.
// decltype (auto) detects the function's return type and allows us to return any return type
template <typename... Args>
decltype (auto) call(const Server& server/*<= temporary*/, const std::string& name, Args&&... args) { // todo: rename to request or nah?
decltype (auto) call(const Server& server, const std::string& name, Args&&... args) { // todo: rename to request or nah?
std::cout << "calling " << name << "\n";
// Print the arguments
//(std::cout << ... << args);
// Get number of arguments
static const size_t arg_count = sizeof...(Args);
std::cout << "arg count: " << arg_count << "\n";
// Check if function is not nullptr before calling it
// Call function (this will work even if a function has zero args :D)
return const_cast<Server&>(server).function_list[name](std::forward<Args>(args)...);
return const_cast<Server&>(server).functions[name](std::forward<Args>(args)...);
}
// For functions without a return value
template <typename... Args>
void call(const std::string& name, Args&&... args) { // todo: rename to request or nah?
}
}
private:
#if defined(NEROSHOP_USE_LIBUV)
uv_tcp_t handle;//uv_tcp_t client;
//uv_udp_t handle; //(for receiving)
//uv_udp_send_t request; //(for sending)
#endif
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
int socket;
char buffer[256];
#endif

@ -67,24 +67,18 @@ neroshop = {
"node.xmr.ru:18081"
},
stagenet = {
"stagenet.community.rino.io:38081",
"xmr-lux.boldsuck.org:38081",
"node2.monerodevs.org:38089",
--"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:38089",
"stagenet.xmr-tw.org:38081",
--"stagenet.xmr.ditatompel.com",
"xmr-lux.boldsuck.org:38081",
--"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:38089",
"node.monerodevs.org:38089",
"http://node.monerodevs.org:38089",
"http://node2.monerodevs.org:38089",
"http://stagenet.community.rino.io:38081",
"http://stagenet.xmr-tw.org:38081",
"http://xmr-lux.boldsuck.org:38081",
"https://xmr-lux.boldsuck.org:38081",
},
testnet = {
"testnet.community.rino.io:28081",
"node2.monerodevs.org:28089",
--"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:28089",
"testnet.xmr-tw.org:28081",
--"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:28089",
--"testnet.xmr.ditatompel.com",
"node.monerodevs.org:28089",
"http://node.monerodevs.org:28089",
"http://node2.monerodevs.org:28089",
"http://testnet.community.rino.io:28081",
"http://testnet.xmr-tw.org:28081",
}
}
}

@ -39,6 +39,9 @@ enum class Currency {
RUB,
PHP,
INR,
// Metals
XAG,
XAU,
// Crypto
XMR,
BTC,
@ -203,6 +206,8 @@ static std::map<std::string, std::tuple<neroshop::Currency, std::string, int>> C
//{ "VUV", { neroshop::Currency::, "Vanuatu Vatu" } },
//{ "WST", { neroshop::Currency::, "Samoa Tala" } },
//{ "XAF", { neroshop::Currency::, "Communauté Financière Africaine (BEAC) CFA Franc BEAC" } },
{ "XAG", { neroshop::Currency::XAG, "Silver", 2 } },
{ "XAU", { neroshop::Currency::XAU, "Gold", 2 } },
//{ "XCD", { neroshop::Currency::, "East Caribbean Dollar" } },
//{ "XDR", { neroshop::Currency::, "International Monetary Fund (IMF) Special Drawing Rights" } },
//{ "XOF", { neroshop::Currency::, "Communauté Financière Africaine (BCEAO) Franc" } },

@ -1,446 +1,8 @@
#include "database.hpp"
neroshop::db::Sqlite3::Sqlite3() : handle(nullptr), opened(false) {}
////////////////////
neroshop::db::Sqlite3::Sqlite3(const std::string& filename) : Sqlite3()
{
if(!open(filename)) {
throw std::runtime_error(std::string("sqlite3_open: ") + std::string(sqlite3_errmsg(handle)));
}
}
////////////////////
neroshop::db::Sqlite3::~Sqlite3() {
close();
}
////////////////////
// SQLite database should only need to be opened once per application session and closed once when the application is terminated
bool neroshop::db::Sqlite3::open(const std::string& filename)
{
if(opened) {
neroshop::print("database is already opened", 2);
return true;
}
if(sqlite3_open(filename.c_str(), &handle) != SQLITE_OK) {
close();
return false;
}
// Enable Write-Ahead Log. This will prevent the database from being locked
if(get_text("PRAGMA journal_mode;") != "wal") {
execute("PRAGMA journal_mode = WAL;"); // requires version 3.7.0 (2010-07-21)
}
// Enable Foreign keys
if(get_integer("PRAGMA foreign_keys;") != 1) {
execute("PRAGMA foreign_keys = ON;"); // requires version 3.6.19 (2009-10-14)
}
opened = true;
return true;
}
////////////////////
void neroshop::db::Sqlite3::close() {
if(!handle) {
return;
}
sqlite3_close(handle);
handle = nullptr;
opened = false;
neroshop::print("database is now closed");
}
////////////////////
void neroshop::db::Sqlite3::execute(const std::string& command)
{
if(!handle) throw std::runtime_error("database is not connected");
char * error_message = 0;
int result = sqlite3_exec(handle, command.c_str(), neroshop::db::Sqlite3::callback, 0, &error_message);
if (result != SQLITE_OK) {
neroshop::print("sqlite3_exec: " + std::string(error_message), 1);
sqlite3_free(error_message);
}
}
////////////////////
void neroshop::db::Sqlite3::execute_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
// Since we don't prepare a statement here, there is no need to finalise it
return;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return;
}
}
// Evaluate the statement
result = sqlite3_step(statement);
if(result != SQLITE_DONE) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return;
}
// Finalize (destroy) the prepared statement
result = sqlite3_finalize(statement);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_finalize: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return;
}
// We are not returning anything, just setting executing queries
}
////////////////////
////////////////////
////////////////////
std::string neroshop::db::Sqlite3::get_sqlite_version() {
return sqlite3_libversion();
}
////////////////////
sqlite3 * neroshop::db::Sqlite3::get_handle() const {
return handle;
}
////////////////////
neroshop::db::Sqlite3 * neroshop::db::Sqlite3::get_database() {
neroshop::db::Sqlite3 * neroshop::get_database() {
std::string database_path = NEROSHOP_DEFAULT_DATABASE_PATH;
std::string database_file = "data.sqlite3";
static neroshop::db::Sqlite3 database_obj { database_file };////static neroshop::db::Sqlite3 database_obj { database_path + "/" + database_file };
return &database_obj;
}
////////////////////
void * neroshop::db::Sqlite3::get_blob(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return nullptr;
}
result = sqlite3_step(statement);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return nullptr;
}
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return nullptr;
}
if(column_type != SQLITE_BLOB) { // NULL is the only other acceptable return type
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return nullptr;
}
void * blob = const_cast<void *>(sqlite3_column_blob(statement, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(statement);
return blob;
}
////////////////////
void * neroshop::db::Sqlite3::get_blob_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return nullptr;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return nullptr;
}
}
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return nullptr;
}
if(column_type != SQLITE_BLOB) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return nullptr;
}
void * blob = const_cast<void *>(sqlite3_column_blob(statement, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(statement);
return blob;
}
////////////////////
std::string neroshop::db::Sqlite3::get_text(const std::string& command) {//const {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * stmt = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &stmt, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return "";
}
result = sqlite3_step(stmt);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(stmt);
return "";
}
int column_type = sqlite3_column_type(stmt, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(stmt);
return "";
}
if(column_type != SQLITE_TEXT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(stmt);
return "";
}
std::string text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(stmt);
return text;
}
////////////////////
std::string neroshop::db::Sqlite3::get_text_params(const std::string& command, const std::vector<std::string>& args) {//const {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return "";
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return "";
}
}
// Evaluate statement
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
// Check the type of the statement's return value
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return "";
}
if(column_type != SQLITE_TEXT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return "";
}
// Finalize (destroy) the prepared statement
std::string text = reinterpret_cast<const char *>(sqlite3_column_text(statement, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(statement);
return text;
}
////////////////////
int neroshop::db::Sqlite3::get_integer(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0;
}
result = sqlite3_step(statement);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return 0;
}
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return 0;
}
if(column_type != SQLITE_INTEGER) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return 0;
}
int number = sqlite3_column_int64(statement, 0);//sqlite3_column_int(statement, 0);
sqlite3_finalize(statement);
return number;
}
////////////////////
int neroshop::db::Sqlite3::get_integer_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return 0;
}
}
// Evaluate statement
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
// Check the type of the statement's return value
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return 0;
}
if(column_type != SQLITE_INTEGER) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return 0;
}
// Finalize (destroy) the prepared statement
int number = sqlite3_column_int64(statement, 0);//sqlite3_column_int(statement, 0);
sqlite3_finalize(statement);
return number;
}
////////////////////
double neroshop::db::Sqlite3::get_real(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * stmt = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &stmt, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0.0;
}
result = sqlite3_step(stmt);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(stmt);
return 0.0;
}
int column_type = sqlite3_column_type(stmt, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(stmt);
return 0.0;
}
if(column_type != SQLITE_FLOAT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(stmt);
return 0.0;
}
double number = sqlite3_column_double(stmt, 0);
sqlite3_finalize(stmt);
return number;
}
////////////////////
double neroshop::db::Sqlite3::get_real_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0.0;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return 0.0;
}
}
// Evaluate statement
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
// Check the type of the statement's return value
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return 0.0;
}
if(column_type != SQLITE_FLOAT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return 0.0;
}
// Finalize (destroy) the prepared statement
double number = sqlite3_column_double(statement, 0);
sqlite3_finalize(statement);
return number;
}
////////////////////
std::vector<std::string> neroshop::db::Sqlite3::get_rows(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * stmt = nullptr;
std::vector<std::string> row_values = {};
// Prepare (compile) statement
if(sqlite3_prepare_v2(handle, command.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return {};
}
// Check whether the prepared statement returns no data (for example an UPDATE)
// "SELECT name FROM users ORDER BY id;" = 1 column
// "SELECT name, age FROM users ORDER BY id;" = 2 columns
// "SELECT * FROM users ORDER BY id;" = all column(s) including the id
if(sqlite3_column_count(stmt) == 0) {
neroshop::print("No data found. Be sure to use an appropriate SELECT statement", 1);
return {};
}
// Get all table values row by row instead of column by column
int result = 0;
while((result = sqlite3_step(stmt)) == SQLITE_ROW) {
for(int i = 0; i < sqlite3_column_count(stmt); i++) {
std::string column_value = (sqlite3_column_text(stmt, i) == nullptr) ? "" : reinterpret_cast<const char *>(sqlite3_column_text(stmt, i));////if(sqlite3_column_text(stmt, i) == nullptr) {throw std::runtime_error("column is NULL");}
row_values.push_back(column_value); //std::cout << sqlite3_column_text(stmt, i) << std::endl;//std::cout << sqlite3_column_name(stmt, i) << std::endl;
// To get a specific column use: if(i == 0) {} or any number
// or call sqlite3_column_name for each column
}
}
if(result != SQLITE_DONE) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
}
sqlite3_finalize(stmt);
return row_values;
}
////////////////////
////////////////////
////////////////////
////////////////////
bool neroshop::db::Sqlite3::is_open() const {
return (opened == true);
}
////////////////////
bool neroshop::db::Sqlite3::table_exists(const std::string& table_name) {
std::string command = "SELECT count(*) FROM sqlite_master WHERE type = 'table' AND name = $1;";
return get_integer_params(command, { table_name });
}
////////////////////
/*bool neroshop::db::Sqlite3::rowid_exists(const std::string& table_name, int rowid) {
int rowid = database->get_integer_params("SELECT id FROM $1 WHERE id = $2", { table_name, rowid });
return (rowid != 0);
}*/
////////////////////
////////////////////
////////////////////
int neroshop::db::Sqlite3::callback(void *not_used, int argc, char **argv, char **az_col_name)
{
int i;
for(i = 0; i < argc; i++) {
std::cout << SQLITE3_TAG << az_col_name[i] << " = " << (argv[i] ? argv[i] : "NULL") << std::endl; // printf("%s = %s\n", azcolname[i], argv[i] ? argv[i] : "nullptr");
}
std::cout << std::endl;
return 0;
}
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////

@ -18,4 +18,9 @@
#include "database/sqlite.hpp"
namespace neroshop {
extern db::Sqlite3 * get_database();
}
#endif

@ -0,0 +1,439 @@
#include "sqlite.hpp"
neroshop::db::Sqlite3::Sqlite3() : handle(nullptr), opened(false) {}
////////////////////
neroshop::db::Sqlite3::Sqlite3(const std::string& filename) : Sqlite3()
{
if(!open(filename)) {
throw std::runtime_error(std::string("sqlite3_open: ") + std::string(sqlite3_errmsg(handle)));
}
}
////////////////////
neroshop::db::Sqlite3::~Sqlite3() {
close();
}
////////////////////
// SQLite database should only need to be opened once per application session and closed once when the application is terminated
bool neroshop::db::Sqlite3::open(const std::string& filename)
{
if(opened) {
neroshop::print("database is already opened", 2);
return true;
}
if(sqlite3_open(filename.c_str(), &handle) != SQLITE_OK) {
close();
return false;
}
// Enable Write-Ahead Log. This will prevent the database from being locked
if(get_text("PRAGMA journal_mode;") != "wal") {
execute("PRAGMA journal_mode = WAL;"); // requires version 3.7.0 (2010-07-21)
}
// Enable Foreign keys
if(get_integer("PRAGMA foreign_keys;") != 1) {
execute("PRAGMA foreign_keys = ON;"); // requires version 3.6.19 (2009-10-14)
}
opened = true;
return true;
}
////////////////////
void neroshop::db::Sqlite3::close() {
if(!handle) {
return;
}
sqlite3_close(handle);
handle = nullptr;
opened = false;
neroshop::print("database is now closed");
}
////////////////////
void neroshop::db::Sqlite3::execute(const std::string& command)
{
if(!handle) throw std::runtime_error("database is not connected");
char * error_message = 0;
int result = sqlite3_exec(handle, command.c_str(), neroshop::db::Sqlite3::callback, 0, &error_message);
if (result != SQLITE_OK) {
neroshop::print("sqlite3_exec: " + std::string(error_message), 1);
sqlite3_free(error_message);
}
}
////////////////////
void neroshop::db::Sqlite3::execute_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
// Since we don't prepare a statement here, there is no need to finalise it
return;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return;
}
}
// Evaluate the statement
result = sqlite3_step(statement);
if(result != SQLITE_DONE) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return;
}
// Finalize (destroy) the prepared statement
result = sqlite3_finalize(statement);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_finalize: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return;
}
// We are not returning anything, just setting executing queries
}
////////////////////
////////////////////
////////////////////
std::string neroshop::db::Sqlite3::get_sqlite_version() {
return sqlite3_libversion();
}
////////////////////
sqlite3 * neroshop::db::Sqlite3::get_handle() const {
return handle;
}
////////////////////
void * neroshop::db::Sqlite3::get_blob(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return nullptr;
}
result = sqlite3_step(statement);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return nullptr;
}
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return nullptr;
}
if(column_type != SQLITE_BLOB) { // NULL is the only other acceptable return type
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return nullptr;
}
void * blob = const_cast<void *>(sqlite3_column_blob(statement, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(statement);
return blob;
}
////////////////////
void * neroshop::db::Sqlite3::get_blob_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return nullptr;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return nullptr;
}
}
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return nullptr;
}
if(column_type != SQLITE_BLOB) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return nullptr;
}
void * blob = const_cast<void *>(sqlite3_column_blob(statement, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(statement);
return blob;
}
////////////////////
std::string neroshop::db::Sqlite3::get_text(const std::string& command) {//const {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * stmt = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &stmt, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return "";
}
result = sqlite3_step(stmt);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(stmt);
return "";
}
int column_type = sqlite3_column_type(stmt, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(stmt);
return "";
}
if(column_type != SQLITE_TEXT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(stmt);
return "";
}
std::string text = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(stmt);
return text;
}
////////////////////
std::string neroshop::db::Sqlite3::get_text_params(const std::string& command, const std::vector<std::string>& args) {//const {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return "";
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return "";
}
}
// Evaluate statement
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
// Check the type of the statement's return value
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return "";
}
if(column_type != SQLITE_TEXT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return "";
}
// Finalize (destroy) the prepared statement
std::string text = reinterpret_cast<const char *>(sqlite3_column_text(statement, 0));//reinterpret_cast<const char *>(sqlite3_column_text16(stmt, 0)); // utf-16
sqlite3_finalize(statement);
return text;
}
////////////////////
int neroshop::db::Sqlite3::get_integer(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0;
}
result = sqlite3_step(statement);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return 0;
}
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return 0;
}
if(column_type != SQLITE_INTEGER) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return 0;
}
int number = sqlite3_column_int64(statement, 0);//sqlite3_column_int(statement, 0);
sqlite3_finalize(statement);
return number;
}
////////////////////
int neroshop::db::Sqlite3::get_integer_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return 0;
}
}
// Evaluate statement
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
// Check the type of the statement's return value
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return 0;
}
if(column_type != SQLITE_INTEGER) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return 0;
}
// Finalize (destroy) the prepared statement
int number = sqlite3_column_int64(statement, 0);//sqlite3_column_int(statement, 0);
sqlite3_finalize(statement);
return number;
}
////////////////////
double neroshop::db::Sqlite3::get_real(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * stmt = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &stmt, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0.0;
}
result = sqlite3_step(stmt);
if (result != SQLITE_ROW) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(stmt);
return 0.0;
}
int column_type = sqlite3_column_type(stmt, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(stmt);
return 0.0;
}
if(column_type != SQLITE_FLOAT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(stmt);
return 0.0;
}
double number = sqlite3_column_double(stmt, 0);
sqlite3_finalize(stmt);
return number;
}
////////////////////
double neroshop::db::Sqlite3::get_real_params(const std::string& command, const std::vector<std::string>& args) {
if(!handle) throw std::runtime_error("database is not connected");
// Prepare statement
sqlite3_stmt * statement = nullptr;
int result = sqlite3_prepare_v2(handle, command.c_str(), -1, &statement, nullptr);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return 0.0;
}
// Bind user-defined parameter arguments
for(int i = 0; i < args.size()/*sqlite3_bind_parameter_count(statement)*/; i++) {
result = sqlite3_bind_text(statement, i + 1, args[i].c_str(), args[i].length(), SQLITE_STATIC);
if(result != SQLITE_OK) {
neroshop::print("sqlite3_bind_*: " + std::string(sqlite3_errmsg(handle)), 1);
sqlite3_finalize(statement);
return 0.0;
}
}
// Evaluate statement
sqlite3_step(statement); // Don't check for error or it'll keep saying: "another row available" or "no more rows available"
// Check the type of the statement's return value
int column_type = sqlite3_column_type(statement, 0);
if(column_type == SQLITE_NULL) {
sqlite3_finalize(statement);
return 0.0;
}
if(column_type != SQLITE_FLOAT) {
neroshop::print("sqlite3_column_type: invalid column return type\ncommand: " + command, 1);
sqlite3_finalize(statement);
return 0.0;
}
// Finalize (destroy) the prepared statement
double number = sqlite3_column_double(statement, 0);
sqlite3_finalize(statement);
return number;
}
////////////////////
std::vector<std::string> neroshop::db::Sqlite3::get_rows(const std::string& command) {
if(!handle) throw std::runtime_error("database is not connected");
sqlite3_stmt * stmt = nullptr;
std::vector<std::string> row_values = {};
// Prepare (compile) statement
if(sqlite3_prepare_v2(handle, command.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
neroshop::print("sqlite3_prepare_v2: " + std::string(sqlite3_errmsg(handle)), 1);
return {};
}
// Check whether the prepared statement returns no data (for example an UPDATE)
// "SELECT name FROM users ORDER BY id;" = 1 column
// "SELECT name, age FROM users ORDER BY id;" = 2 columns
// "SELECT * FROM users ORDER BY id;" = all column(s) including the id
if(sqlite3_column_count(stmt) == 0) {
neroshop::print("No data found. Be sure to use an appropriate SELECT statement", 1);
return {};
}
// Get all table values row by row instead of column by column
int result = 0;
while((result = sqlite3_step(stmt)) == SQLITE_ROW) {
for(int i = 0; i < sqlite3_column_count(stmt); i++) {
std::string column_value = (sqlite3_column_text(stmt, i) == nullptr) ? "" : reinterpret_cast<const char *>(sqlite3_column_text(stmt, i));////if(sqlite3_column_text(stmt, i) == nullptr) {throw std::runtime_error("column is NULL");}
row_values.push_back(column_value); //std::cout << sqlite3_column_text(stmt, i) << std::endl;//std::cout << sqlite3_column_name(stmt, i) << std::endl;
// To get a specific column use: if(i == 0) {} or any number
// or call sqlite3_column_name for each column
}
}
if(result != SQLITE_DONE) {
neroshop::print("sqlite3_step: " + std::string(sqlite3_errmsg(handle)), 1);
}
sqlite3_finalize(stmt);
return row_values;
}
////////////////////
////////////////////
////////////////////
////////////////////
bool neroshop::db::Sqlite3::is_open() const {
return (opened == true);
}
////////////////////
bool neroshop::db::Sqlite3::table_exists(const std::string& table_name) {
std::string command = "SELECT count(*) FROM sqlite_master WHERE type = 'table' AND name = $1;";
return get_integer_params(command, { table_name });
}
////////////////////
/*bool neroshop::db::Sqlite3::rowid_exists(const std::string& table_name, int rowid) {
int rowid = database->get_integer_params("SELECT id FROM $1 WHERE id = $2", { table_name, rowid });
return (rowid != 0);
}*/
////////////////////
////////////////////
////////////////////
int neroshop::db::Sqlite3::callback(void *not_used, int argc, char **argv, char **az_col_name)
{
int i;
for(i = 0; i < argc; i++) {
std::cout << SQLITE3_TAG << az_col_name[i] << " = " << (argv[i] ? argv[i] : "NULL") << std::endl; // printf("%s = %s\n", azcolname[i], argv[i] ? argv[i] : "nullptr");
}
std::cout << std::endl;
return 0;
}
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////

@ -18,7 +18,9 @@
#include "../debug.hpp"
namespace neroshop {
namespace db {
class Sqlite3 {
public:
Sqlite3();
@ -50,10 +52,8 @@ private:
bool opened;
static int callback(void *not_used, int argc, char **argv, char **az_col_name);
};
}
}
/*
// Would it be more appropriate if the server handles the database queries instead of the client?
// The client would send the server a request to execute a SQLite query. This sounds good.
*/
#endif

@ -11,14 +11,42 @@
#define NEROSHOP_LOG_FILE "log.txt"
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <iomanip> // std::put_time
namespace neroshop {
inline void print(const std::string& text, int code = 0) { // 0=normal, 1=error, 2=warning, 3=success,
if(code == 0) std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;37;49m" << text << "\033[0m" << std::endl;
if(code == 1) std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;91;49m" << text << "\033[0m" << std::endl;
if(code == 2) std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;33;49m" << text << "\033[0m" << std::endl;
if(code == 3) std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;32;49m" << text << "\033[0m" << std::endl;
if(code == 4) std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;34;49m" << text << "\033[0m" << std::endl;
enum log_priority {
trace, error, warn, info
};
static void logger(log_priority priority, const std::string& message) {
std::ofstream file(std::string(NEROSHOP_LOG_FILE).c_str(), std::ios_base::app);
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now); // current time
std::stringstream ss;
ss << std::put_time(std::localtime(&in_time_t), std::string("[%Y-%m-%d %H:%M:%S %p]").c_str());
switch (priority) {
case trace: file << ss.str() << "[Trace]: "; break;
//case debug: file << ss.str() << "[Debug:] "; break;
case info: file << ss.str() << "[Info:] "; break;
case warn: file << ss.str() << "[Warn]: "; break;
case error: file << ss.str() << "[Error]: "; break;
//case critical: file << ss.str() << "[Critical]: "; break;
}
file << message << "\n";
file.close();
}
inline void print(const std::string& text, int code = 0, bool log_msg = true) { // 0=normal, 1=error, 2=warning, 3=success,
log_priority verbosity;
if(code == 0) { std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;37;49m" << text << "\033[0m\n"; verbosity = log_priority::trace; }
if(code == 1) { std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;91;49m" << text << "\033[0m\n"; verbosity = log_priority::error; }
if(code == 2) { std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;33;49m" << text << "\033[0m\n"; verbosity = log_priority::warn; }
if(code == 3) { std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;32;49m" << text << "\033[0m\n"; verbosity = log_priority::info; }
if(code == 4) { std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;34;49m" << text << "\033[0m\n"; verbosity = log_priority::info; }
if(log_msg) logger(verbosity, text);
}
inline void io_write(const std::string& text) {// like print but without a newline
std::cout << "\033[1;35;49m" << "[neroshop]: " << "\033[1;37;49m" << text << "\033[0m";

@ -32,7 +32,7 @@ neroshop::Item::~Item() {
}
////////////////////
void neroshop::Item::register_item(const std::string& name, const std::string& description, double price, double weight, double length, double width, double height, const std::string& condition, const std::string& product_code) {
db::Sqlite3 * database = db::Sqlite3::get_database();
db::Sqlite3 * database = neroshop::get_database();
// if item is already registered, then exit function
if(!database->table_exists("products")) {
neroshop::print("register_item: table \"products\" is missing", 1);

@ -35,6 +35,8 @@ const std::map<neroshop::Currency, QString> CURRENCY_TO_ID{
{neroshop::Currency::PHP, "2803"},
{neroshop::Currency::INR, "2796"},
//{neroshop::Currency::},
//{neroshop::Currency::XAG, ""},
//{neroshop::Currency::XAU, ""},
{neroshop::Currency::BTC, "1"},
{neroshop::Currency::ETH, "1027"},
{neroshop::Currency::LTC, "2"},

@ -28,6 +28,9 @@ const std::map<neroshop::Currency, QString> CURRENCY_TO_ID{
{neroshop::Currency::MXN, "MXN"},
{neroshop::Currency::NZD, "NZD"},
{neroshop::Currency::SEK, "SEK"},
{neroshop::Currency::XAG, "XAG"},
{neroshop::Currency::XAU, "XAU"},
};
const std::map<neroshop::Currency, QString> CRYPTO_TO_ID{

@ -0,0 +1,140 @@
#include "rpc.hpp"
std::string neroshop::rpc::translate(const std::string& sql) {
std::string request = "";
#if defined(NEROSHOP_USE_QT)
QJsonObject request_object; // JSON-RPC Request object
request_object.insert(QString("jsonrpc"), QJsonValue("2.0"));
request_object.insert(QString("method"), QJsonValue("query"));////QJsonValue(QString::fromStdString(method_type)));
QJsonObject params_object;////QJsonArray params_array; // can be an array or object
params_object.insert(QString("sql"), QJsonValue(QString::fromStdString(sql)));////params_array.insert(params_array.size(), QJsonValue("arg1")); // based on the number of parameter args
request_object.insert(QString("params"), QJsonValue(params_object));
// Generate random number for id (id can be either a string or an integer or null which is not recommended)
std::random_device rd; // obtain a random number from hardware
std::mt19937 gen(rd()); // seed the generator
std::uniform_int_distribution<> distr(1, 9); // define the range
std::string random_id;
for(int n = 0; n < 10; ++n) {
int random_integer = distr(gen); // generate numbers
random_id = random_id + std::to_string(random_integer); // append 10 different numbers to generate a unique id
}
request_object.insert(QString("id"), QJsonValue(QString::fromStdString(random_id))); // https://stackoverflow.com/questions/2210791/json-rpc-what-is-the-id-for
// Convert JSON to string then display it (for debugging purposes)
QJsonDocument json_doc(request_object);
QString json_str = json_doc.toJson();////(QJsonDocument::Compact); // https://doc.qt.io/qt-6/qjsondocument.html#JsonFormat-enum
request = json_str.toStdString();
#else
nlohmann::json json;
json["jsonrpc"] = "2.0";
json["method"] = "query";////get_method(sql);
json["params"]["sql"] = sql;
// Generate random number for id (id can be either a string or an integer or null which is not recommended)
std::random_device rd; // obtain a random number from hardware
std::mt19937 gen(rd()); // seed the generator
std::uniform_int_distribution<> distr(1, 9); // define the range
std::string random_id;
for(int n = 0; n < 10; ++n) {
int random_integer = distr(gen); // generate numbers
random_id = random_id + std::to_string(random_integer); // append 10 different numbers to generate a unique id
}
json["id"] = random_id;
// Dump JSON to string
request = json.dump(4);////json.dump();
#endif
#ifdef NEROSHOP_DEBUG
std::cout << "\"" << sql << "\" has been translated to: \n" << request << std::endl;
#endif
return request;
}
std::string neroshop::rpc::translate(const std::string& sql, const std::vector<std::string>& args) {
std::string request = "";
#if defined(NEROSHOP_USE_QT)
QJsonObject request_object; // JSON-RPC Request object
request_object.insert(QString("jsonrpc"), QJsonValue("2.0"));
request_object.insert(QString("method"), QJsonValue("query"));
QJsonObject params_object;
params_object.insert(QString("sql"), QJsonValue(QString::fromStdString(sql))); // eg. "SELECT * FROM products WHERE condition = $1;"
params_object.insert(QString("count"), static_cast<int>(args.size()));
for(int index = 0; index < args.size(); index++) {
std::string arg_key = /*"" + */std::to_string(index + 1);
std::string arg_value = args[index];
params_object.insert(QString::fromStdString(arg_key), QJsonValue(QString::fromStdString(arg_value)));
std::cout << "" << (index + 1) << "= " << arg_value << "\n";
}
request_object.insert(QString("params"), QJsonValue(params_object));
// Generate random number for id (id can be either a string or an integer or null which is not recommended)
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr(1, 9);
std::string random_id;
for(int n = 0; n < 10; ++n) {
int random_integer = distr(gen);
random_id = random_id + std::to_string(random_integer);
}
request_object.insert(QString("id"), QJsonValue(QString::fromStdString(random_id)));
// Convert JSON to string
QJsonDocument json_doc(request_object);
QString json_str = json_doc.toJson();
request = json_str.toStdString();
#else
nlohmann::json json;
json["jsonrpc"] = "2.0";
json["method"] = "query";////get_method(sql);
json["params"]["sql"] = sql;
json["params"]["count"] = args.size();
for(int index = 0; index < args.size(); index++) {
std::string arg_key = std::to_string(index + 1);
std::string arg_value = args[index];
json["params"][arg_key] = arg_value;
}
// Generate random number for id (id can be either a string or an integer or null which is not recommended)
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr(1, 9);
std::string random_id;
for(int n = 0; n < 10; ++n) {
int random_integer = distr(gen);
random_id = random_id + std::to_string(random_integer);
}
json["id"] = random_id;
// Dump JSON to string
request = json.dump(4);////json.dump();
#endif
#ifdef NEROSHOP_DEBUG
std::cout << "\"" << sql << "\" has been translated to: \n" << request << std::endl;
#endif
return request;
}
void neroshop::rpc::request(const std::string& json) { // TODO: this function should return a json_rpc response from the server
// Get the json which is basically a translated sqlite query or a translate method string + args
// Passing the data onto the server
////zmq_send (requester, request.c_str(), request.size(), 0);
// The server will then execute the data
// Lastly, the server will return any results
} // Usage: neroshop::rpc::request(neroshop::rpc::translate("SELECT * FROM users;"));
void request_batch(const std::vector<std::string>& json_batch) {
}
void neroshop::rpc::respond(const std::string& json) {
std::string response = "";
#if defined(NEROSHOP_USE_QT)
#endif
}
void neroshop::rpc::respond_batch(const std::vector<std::string>& json_batch) {
}
std::string neroshop::rpc::get_method(const std::string& sql) {
std::string first_word = sql.substr(0, sql.find_first_of(" "));
if(is_valid_method(first_word)) return first_word;
return "";
}
bool neroshop::rpc::is_valid_method(const std::string& method) {
return ((std::find(sql_methods.begin(), sql_methods.end(), method) != sql_methods.end()) || (std::find(methods.begin(), methods.end(), method) != methods.end()));
}

@ -0,0 +1,48 @@
#pragma once
#if defined(NEROSHOP_USE_QT)
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#else
#include <nlohmann/json.hpp>
#endif
#include <iostream>
#include <random>
// uncomment to disable assert()
// #define NDEBUG
////#include <cassert>
// neroshop JSON-RPC API // TODO: translate method names like get_product_ratings (along with the args) into json request strings via command line
namespace neroshop {
namespace rpc {
static std::vector<std::string> methods = {};//static std::unordered_map<std::string, std::function<void(int)>> methods_cmd
static std::vector<std::string> sql_methods = { // TODO: rename to query_type?
"SELECT", // extracts data from a database
"UPDATE", // updates data in a database
"DELETE", // deletes data from a database
"INSERT", // "INSERT INTO" - inserts new data into a database
"CREATE", // "CREATE DATABASE" - creates a new database // "CREATE TABLE" - creates a new table // "CREATE INDEX" - creates an index (search key)
"ALTER", // "ALTER DATABASE" - modifies a database // "ALTER TABLE" - modifies a table
"DROP", // "DROP TABLE" - deletes a table // "DROP INDEX" - deletes an index
};
static std::string get_method(const std::string& sql);
static bool is_valid_method(const std::string& method);
static std::string translate(const std::string& sql); // converts an sqlite query to a json_rpc request message
static std::string translate(const std::string& sql, const std::vector<std::string>& args);
template <typename... Args>
static std::string translate_cmd(const std::string& method, Args&&... args) { // converts a method (string) and its argument(s) into a json_rpc response message via the command line
// accessing the number of args: static const size_t arg_count = sizeof...(Args);
//if(method == "get_whatever") { ...
// accessing the arguments: functions[name](std::forward<Args>(args)...);
return ""; // TEMPORARY
}
static void request(const std::string& json); // for client - to send requests
static void request_batch(const std::vector<std::string>& json_batch); // sends a batch of json_rpc requests to the server and expects a batch of json_rpc responses
static void respond(const std::string& json); // for server - to respond to requests
static void respond_batch(const std::vector<std::string>& json_batch);
}
}

@ -26,7 +26,7 @@ neroshop::Seller::~Seller() {
////////////////////
void neroshop::Seller::list_item(const std::string& product_id, unsigned int quantity, double price, const std::string& currency, const std::string& condition, const std::string& location)
{
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
std::string listing_uuid = database->get_text_params("INSERT INTO listings (uuid, product_id, seller_id, quantity, price, currency, condition, location, date) " // date should always be last
@ -342,7 +342,7 @@ unsigned int neroshop::Seller::get_total_ratings() const {
}
////////////////////
unsigned int neroshop::Seller::get_reputation() const {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Get seller reputation as percentage
unsigned int ratings_count = database->get_integer_params("SELECT COUNT(*) FROM seller_ratings WHERE seller_id = $1", { get_id() });
@ -467,7 +467,7 @@ std::vector<int> neroshop::Seller::get_pending_customer_orders() {
// getters - sales and statistics-related stuff
////////////////////
unsigned int neroshop::Seller::get_products_count() const {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
int products_listed = database->get_integer_params("SELECT COUNT(product_id) FROM listings WHERE seller_id = $1;", { get_id() });
@ -591,7 +591,7 @@ bool neroshop::Seller::has_wallet_synced() const {
// callbacks
////////////////////
neroshop::User * neroshop::Seller::on_login(const neroshop::Wallet& wallet) { // assumes user data already exists in database
/*neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
/*neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");*/
//std::string user_id = database->get_text_params("SELECT monero_address FROM users WHERE name = $1", { username });
std::string monero_address = wallet.get_monero_wallet()->get_primary_address();

@ -13,7 +13,7 @@ neroshop::Server::Server()
neroshop::print("uv_tcp_init error: " + std::string(uv_strerror(result)), 1);
}
#endif
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
socket = ::socket(AF_INET, SOCK_STREAM, 0);
if (socket < 0) {
std::cerr << "Could not create socket" << std::endl;
@ -25,22 +25,6 @@ neroshop::Server::Server()
std::cerr << "Could not set socket options" << std::endl;
}
#endif
// Create a new raft server
/*int server_count = 1;
void * connection_user_data = nullptr; // pointer to userdata
// Generate random number to be used for node_id
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_real_distribution<double> dist(0, 100);
int node_id = static_cast<int>(dist(mt)); // SHOULD be random
bool peer_is_self = true;
raft = raft_new();
for(int i = 0; i < server_count; ++i) {
raft_add_node(static_cast<raft_server_t*>(raft), connection_user_data, node_id, peer_is_self);
std::cout << "raft node has been added\n";
}*/
}
////////////////////
neroshop::Server::~Server() {
@ -66,7 +50,7 @@ bool neroshop::Server::bind(unsigned int port)
neroshop::print("uv_tcp_bind error: " + std::string(uv_strerror(result)), 1);
}
#endif
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(struct sockaddr_in));//bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_port = htons(port);
@ -91,7 +75,7 @@ bool neroshop::Server::listen()
return false;
}
#endif
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
if(::listen(socket, SOMAXCONN) < 0) {//SOMAXCONN=4096 // number of requests to listen to at a time
std::cerr << "Cannot listen for a connection" << std::endl;
shutdown();
@ -103,7 +87,7 @@ bool neroshop::Server::listen()
}
////////////////////
bool neroshop::Server::accept() {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
struct sockaddr_in client_addr;
socklen_t clilen = sizeof(client_addr);
/*int */client_socket = ::accept(socket, (struct sockaddr *) &client_addr, &clilen);
@ -126,7 +110,7 @@ bool neroshop::Server::accept_all()
}
////////////////////
void neroshop::Server::write(const std::string& message) {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
ssize_t write_result = ::write(client_socket, message.c_str(), message.length()/*buffer, strlen(buffer)*/);
if(write_result < 0) { // -1 = error
std::cout << "Server unable to write to client" << std::endl;
@ -136,7 +120,7 @@ void neroshop::Server::write(const std::string& message) {
////////////////////
std::string neroshop::Server::read() // receive data
{
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
memset(buffer, 0, 256); // clear buffer (fills buffer with 0's) before reading into buffer
ssize_t read_result = ::read(client_socket, buffer, 255);//::read(client_socket, (void *)buffer_new.c_str(), buffer_new.length()); // https://stackoverflow.com/questions/10105591/is-it-possible-to-use-an-stdstring-for-read // #include <unistd.h>
if(read_result < 0) { // -1 = error
@ -159,75 +143,18 @@ std::string neroshop::Server::read() // receive data
}
////////////////////
void neroshop::Server::close() {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
::close(socket);
#endif
}
////////////////////
void neroshop::Server::shutdown() {
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
::shutdown(socket, SHUT_RDWR); // SHUT_RD, SHUT_WR, SHUT_RDWR
#endif
}
////////////////////
////////////////////
////////////////////
#if defined(NEROSHOP_USE_LIBUV)
void neroshop::Server::on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
neroshop::print("New connection error: " + std::string(uv_strerror(status)), 1);
////uv_close((uv_handle_t*) client, NULL); // close tcp connection
// error!
return;
}
uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
uv_tcp_init(uv_default_loop()/*loop*/, client);
if (uv_accept(server, (uv_stream_t*) client) == 0) { // success
uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);//std::cout << "Got a connection from a peer/client!\n";//neroshop::print(std::string("\033[0;37mReceived a connection from ") + "<ip>" + ":\033[0;36m" + "<port>");
}
else {
uv_close((uv_handle_t*) client, NULL);
}
}
////////////////////
void neroshop::Server::alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = (char *)malloc(suggested_size);
buf->len = suggested_size;
//memset(buf->base, 0, buf->len);
}
////////////////////
void neroshop::Server::echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
if(nread < 0) {//== -1) {
if(nread != UV_EOF) neroshop::print("Read error: " + std::string(uv_err_name(nread)), 1);
uv_close((uv_handle_t*)client, NULL);
return;
}
std::cout << "server_read_message_from_client: " << buf->base << std::endl;
// Write what the server read from the client (works so far)
//if (nread > 0) {
uv_write_t *write_req = (uv_write_t*)malloc(sizeof(uv_write_t));
write_req->data = (void*)buf->base;
const_cast<uv_buf_t *>(buf)->len = nread;
uv_write(write_req, client, buf, 1, echo_write);
//return;
//}
////free(buf->base);
}
////////////////////
void neroshop::Server::echo_write(uv_write_t *req, int status) {
if(status == -1) {
neroshop::print("Write error: " + std::string(uv_strerror(status)));
}
char *buffer_base = (char*) req->data;
free(buffer_base);
free(req);
}
#endif
////////////////////
//template<typename F> void neroshop::Server::bind(const std::string& name, F func) {//F response) {
// if client requests a string e.g "LOGIN" then server will respond with login()
//response();

@ -1,26 +1,34 @@
#ifndef SERVER_HPP_NEROSHOP
#define SERVER_HPP_NEROSHOP
//#if defined(NEROSHOP_USE_LIBUV)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#endif
#if defined(NEROSHOP_USE_LIBZMQ)
#include <zmq.h>
#endif
#if defined(NEROSHOP_USE_LIBUV)
#include <uv.h>
//#endif
#endif
#include <cstdlib>
extern "C" {
#include <raft.h>
}
#include <unordered_map> // std::unordered_map
#include <functional> // std::function
//#if defined(__cplusplus) && (__cplusplus >= 201703L)
#include <any> // std::any (C++17)
//#endif
#include <stdexcept> // std::runtime_error
#include <cstring> // memset
#include <random> // std::random_device
#if defined(__gnu_linux__)
#include <unistd.h> // ::close
#endif
#include "database.hpp"
#include "debug.hpp"
@ -58,7 +66,7 @@ public:
//if constexpr (std::is_same<F, std::function<void ()>>::value) {
// std::cout << "F is a valid function\n";
//}
function_list[name] = functor;
functions[name] = functor;
}
//! \brief Unbinds a functor binded to a name.
@ -67,30 +75,25 @@ public:
//!
//! \param name The name of the functor.
void unbind(std::string const &name) {
function_list[name] = nullptr; // todo: remove element from unordered_map c++ the proper way
functions.erase(functions.find(name));//functions[name] = nullptr; // todo: remove element from unordered_map c++ the proper way
}
std::unordered_map<std::string, std::any> get_functions() {
return functions;
}
//private:
#if defined(NEROSHOP_USE_LIBUV)
uv_tcp_t * handle_tcp;
uv_udp_t * handle_udp;
// callbacks (libuv)
static void on_new_connection(uv_stream_t *server, int status);
static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf);
static void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf);
static void echo_write(uv_write_t *req, int status);
#endif
#if defined(__gnu_linux__)
#if defined(__gnu_linux__) && defined(NEROSHOP_USE_SYSTEM_SOCKETS)
int socket;
char buffer[256];
int client_socket;
#endif
void * raft;
raft_server_t* raft;
// functors
//#if defined(__cplusplus) && (__cplusplus < 201703L)
//std::unordered_map<std::string, std::function<void()>/*adaptor_type*/> function_list;
//std::unordered_map<std::string, std::function<void()>/*adaptor_type*/> functions;
//#endif
//#if defined(__cplusplus) && (__cplusplus >= 201703L)
std::unordered_map<std::string, std::any> function_list;
std::unordered_map<std::string, std::any> functions;
//#endif
// ??
/*template< class R, class... Args >

@ -22,7 +22,7 @@ neroshop::User::~User()
////////////////////
// buyers can only rate seller they have purchased from!!
void neroshop::User::rate_seller(const std::string& seller_id, int score, const std::string& comments, const std::string& signature) { // perfected 99.9%!!
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// seller_id cannot be 0 (0 = invalid id)
if(seller_id.empty()) return;
@ -73,7 +73,7 @@ void neroshop::User::rate_seller(const std::string& seller_id, int score, const
////////////////////
////////////////////
void neroshop::User::rate_item(const std::string& product_id, int stars, const std::string& comments, const std::string& signature) { // perfected 99%!!!
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// If item is not registered
if(product_id.empty()) return; // exit function
@ -169,7 +169,7 @@ void neroshop::User::convert() {
////////////////////
void neroshop::User::delete_account() {
if(!is_logged()) {neroshop::print("You are not logged in", 2);return;} // must be logged in to delete your account
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
////database->execute("BEGIN;"); // not necessary unless doing multiple operations
////database->execute("SAVEPOINT before_account_deletion_savepoint;");//ROLLBACK TO before_account_deletion_savepoint;
@ -366,7 +366,7 @@ void neroshop::User::load_favorites() {
// avatar-related stuff here
////////////////////
void neroshop::User::upload_avatar(const std::string& filename) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//----------------------------
//unsigned int user_id = 1;
@ -450,7 +450,7 @@ void neroshop::User::upload_avatar(const std::string& filename) {
}
////////////////////
bool neroshop::User::export_avatar() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//----------------------------
// Check if avatar column exists first and that its not null
@ -811,7 +811,7 @@ bool neroshop::User::has_email() const {
}
////////////////////
bool neroshop::User::has_avatar() const {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// If id is zero (this means the user does not exist)
if(this->id.empty()) return false;

@ -195,6 +195,7 @@ std::string neroshop::Wallet::upload(bool open, std::string password) { // opens
void neroshop::Wallet::transfer(const std::string& address, double amount) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
std::packaged_task<void(void)> transfer_task([this, address, amount]() -> void {
// Convert monero to piconero
double piconero = 0.000000000001;
uint64_t monero_to_piconero = amount / piconero; //std::cout << neroshop::string::precision(amount, 12) << " xmr to piconero: " << monero_to_piconero << "\n";
@ -231,6 +232,12 @@ void neroshop::Wallet::transfer(const std::string& address, double amount) {
//uint64_t deducted_amount = (monero_to_piconero + fee);
std::string tx_hash = monero_wallet_obj->relay_tx(*sent_tx); // recipient receives notification within 5 seconds
std::cout << "Tx hash: " << tx_hash << "\n";
});
std::future<void> future_result = transfer_task.get_future();
// move the task (function) to a separate thread to prevent blocking of the main thread
std::thread worker(std::move(transfer_task));
worker.detach(); // join may block but detach won't//void transfer_result = future_result.get();
}
////////////////////
std::string neroshop::Wallet::sign_message(const std::string& message, monero_message_signature_type signature_type) const {

@ -1,22 +1,13 @@
#include <dirent.h>
#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <string>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include <vector>
#include <iostream>
// neroshop
#include "../core/server.hpp"
#include "../core/database.hpp"
#include "../core/debug.hpp"
//#include "db2.hpp" // daemon should handle database server requests from the client ??
#define NEROMON_TAG "\033[1;95m[neromon]:\033[0m "
// Daemon will handle database server requests from the client
using namespace neroshop;
Server * server;
@ -28,98 +19,6 @@ void close_server() {
std::cout << NEROMON_TAG "\033[1;91mdisconnected\033[0m" << std::endl;
}
////////////////////
#define DEFAULT_PORT 7000
#define DEFAULT_BACKLOG 128
////////////////////
// Alloc callback (uv_alloc_cb)
// suggested_size で渡された領域を確保
void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = (char *)malloc(suggested_size);
buf->len = suggested_size;
////memset(buf->base, 0, buf->len);
}
////////////////////
void on_client_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
void on_client_write(uv_write_t *req, int status);
////////////////////
// Read callback (uv_read_cb)
void on_client_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
if(nread < 0) {//== -1) {
neroshop::print("error on_client_read", 1);
uv_close((uv_handle_t*)stream, NULL);
return;
}
// クライアントから受け取ったデータ
// データは buf->base に格納されている。
//if(nread > 0) {
std::cout << "server_read_message_from_client: " << buf->base << std::endl; // there is data available
// ファイルを開く (buf がファイルの場合)
////int mode = 0;
////uv_fs_open(uv_default_loop(), &open_req, filename, O_RDONLY, mode, on_file_open);
//}
// Write what the server read from the client (works)
uv_write_t *write_req = (uv_write_t*)malloc(sizeof(uv_write_t));
write_req->data = (void*)buf->base;
const_cast<uv_buf_t *>(buf)->len = nread;
uv_write(write_req, stream, buf, 1, on_client_write);
}
////////////////////
// Write callback (uv_write_cb)
void on_client_write(uv_write_t *req, int status) { // change to on_write_end
if(status == -1) {
neroshop::print("error on_client_write", 1);
return;
}
char *buffer_base = (char*) req->data;
free(buffer_base);
free(req);
}
////////////////////
// Shutdown callback
void shutdown_cb(uv_shutdown_t *req, int status) {
}
////////////////////
uv_loop_t * loop;
// Connection callback - received an incoming connection
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
neroshop::print("New connection error: " + std::string(uv_strerror(status)), 1);
////uv_close((uv_handle_t*) client, NULL); // close tcp connection
// error!
return;
}
// クライアントを保持するためのメモリを確保
uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
// loop への登録
uv_tcp_init(loop, client);
// accept the connection
if (uv_accept(server, (uv_stream_t*) client) == 0) { // success
//----------------------------------------
/*std::string message = "Server: Welcome to the server";//NEROMON_TAG "\033[1;32mconnected\033[0m";
// write to client
char buffer[100];
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
buf.len = message.length();
buf.base = const_cast<char *>(message.c_str());
uv_write_t write_req;
// writes
int buf_count = 1; // number of times we write
// To write to the client
uv_write(&write_req, (uv_stream_t*)client, &buf, buf_count, on_client_write);*/
//----------------------------------------
// reading from client
uv_read_start((uv_stream_t*) client, alloc_buffer, on_client_read);//std::cout << "Got a connection from a peer/client!\n";//neroshop::print(std::string("\033[0;37mReceived a connection from ") + "<ip>" + ":\033[0;36m" + "<port>"); // daemons cannot use std::cout so this is useless
}
else {
uv_close((uv_handle_t*) client, NULL);
}
// then continue reading for any client messages
}
///////////////////
void do_heartbeat()
{
@ -131,130 +30,33 @@ void do_heartbeat()
//new_client.join();
server->write(NEROMON_TAG "\033[1;32mconnected\033[0m"); // write to client once
} //else exit(0);
std::cout << server->read() << std::flush << std::endl;
std::cout << server->read() << std::endl;
}
// For security purposes, we don't allow any arguments to be passed into the daemon
int main(void)
{
// Fork the current process. The parent process continues with a process ID
// greater than 0. A process ID lower than 0 indicates a failure in either
// process.
pid_t pid = fork();
if (pid > 0) exit(EXIT_SUCCESS); else if (pid < 0) exit(EXIT_FAILURE);
// TODO: I think we should log to ~/.config/neroshop/
// The parent process has now terminated, and the forked child process will
// continue (the pid of the child process was 0). Since the child process is
// a daemon, the umask needs to be set so files and logs can be written.
umask(0);
// Open system logs for the child process
openlog("neromon", LOG_NOWAIT | LOG_PID, LOG_USER);
syslog(LOG_NOTICE, "Successfully started neromon");
// Generate a session ID for the child process and ensure it is valid.
if(setsid() < 0) {
// Log failure and exit
syslog(LOG_ERR, "Could not generate session ID for child process");
// If a new session ID could not be generated, we must terminate the child
// process or it will be orphaned.
exit(EXIT_FAILURE);
}
// Change the current working directory to a directory guaranteed to exist
if (chdir("/") < 0) {
// Log failure and exit
syslog(LOG_ERR, "Could not change working directory to /");
// If our guaranteed directory does not exist, terminate the child process
// to ensure the daemon has not been hijacked.
exit(EXIT_FAILURE);
}
//---
// A daemon cannot use the terminal, so close standard file descriptors for security reasons
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// Daemon-specific intialization should go here
const int SLEEP_INTERVAL = 1;
//////////////////////////////////////////////////
// Start server
std::atexit(close_server);
server = new Server();
int server_port = 1234;//(std::stoi(port));
int server_port = 40441;//1234;//(std::stoi(port));
if(server->bind(server_port)) {
// Daemon cannot write to stdin, so we must use the Server::write function
//std::cout << std::endl << NEROMON_TAG "\033[1;97mbound to port " + std::to_string(server_port) << "\033[0m" << std::endl;
server->write(NEROMON_TAG "\033[1;97mbound to port " + std::to_string(server_port) + "\033[0m");
server->write(NEROMON_TAG "\033[1;97mbound to port " + std::to_string(server_port) + "\033[0m\n");
}
server->listen(); // listens for any incoming connection
const int SLEEP_INTERVAL = 1;
// Enter daemon loop
while(true) {
// Execute daemon heartbeat
do_heartbeat();
// Sleep for a period of time
sleep(SLEEP_INTERVAL);
}
/////////////////////////////////////////////////////////////////
//loop = uv_default_loop();
/////////////////////////////////////////////////////////////////
/*uv_pipe_t server_pipe;
uv_pipe_init(loop, &server_pipe, 1); // 1 = ipc
////signal(SIGINT, remove_sock);
int resultp = 0;
if((resultp = uv_pipe_bind(&server_pipe, "echo.sock"))) {
fprintf(stderr, "Bind error %s\n", uv_err_name(resultp));
return 1;
}
if((resultp = uv_listen((uv_stream_t*) &server_pipe, 128, on_new_connection))) {
fprintf(stderr, "Listen error %s\n", uv_err_name(resultp));
return 2;
}*/
//uv_read_start((uv_stream_t*)&server_pipe, alloc_buffer, on_new_connection);
/////////////////////////////////////////////////////////////////
// Start server (libuv)
/*uv_tcp_t server;
int result = uv_tcp_init(loop, &server);
if(result != 0) {
neroshop::print("uv_tcp_init error: " + std::string(uv_strerror(result)), 1);
}
struct sockaddr_in bind_addr;
std::string ipv4_default = "0.0.0.0";
std::string ipv6_default = "::/0"; // ::/0 is the IPv6 equivalent of 0.0.0.0/0 (IPv4)
std::string ipv4_localhost = "127.0.0.1";
std::string ipv6_localhost = "::1"; // ::1/128 is the IPv6 equivalent of 127.0.0.1/8 (IPv4)
int port = DEFAULT_PORT;//1234;
result = uv_ip4_addr(ipv4_localhost.c_str(), port, &bind_addr);
if(result != 0) {
neroshop::print("uv_ip4_addr error: " + std::string(uv_strerror(result)), 1);
}
// bind server to a port
result = uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0);
if(result != 0) {
neroshop::print("uv_tcp_bind error: " + std::string(uv_strerror(result)), 1);
}
std::cout << std::endl << NEROMON_TAG "\033[1;97mbound to port " + std::to_string(port) << "\033[0m" << std::endl;
// listen for any incoming connections
if((result = uv_listen((uv_stream_t*)&server, DEFAULT_BACKLOG, on_new_connection)) != 0) {
neroshop::print("uv_listen error: " + std::string(uv_strerror(result)), 1);
return 1;
}*/
// Simplified (wrapped) version
/*Server * server = new Server(); // uv_tcp_init called here
server->bind(DEFAULT_PORT);//(1234);
server->listen();*/
//return uv_run(loop, UV_RUN_DEFAULT);
while(true) {
// Execute daemon heartbeat
do_heartbeat();
// Sleep for a period of time
#ifdef _WIN32
Sleep(SLEEP_INTERVAL);
#else
sleep(SLEEP_INTERVAL);
#endif
}
return 0;
}

@ -3,6 +3,7 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QFile>
#include <future>
#include <thread>
@ -53,7 +54,7 @@ bool neroshop::Backend::isSupportedCurrency(const QString& currency) const {
void neroshop::Backend::initializeDatabase() {
std::cout << "sqlite3 v" << db::Sqlite3::get_sqlite_version() << std::endl;
db::Sqlite3 * database = db::Sqlite3::get_database();
db::Sqlite3 * database = neroshop::get_database();
database->execute("BEGIN;");
//-------------------------
// Todo: Make monero_address the primary key and remove id. Also, replace all foreign key references from id to monero_address
@ -99,7 +100,7 @@ void neroshop::Backend::initializeDatabase() {
database->execute("ALTER TABLE listings ADD COLUMN condition TEXT;"); // item condition
database->execute("ALTER TABLE listings ADD COLUMN location TEXT;");
//database->execute("ALTER TABLE listings ADD COLUMN last_updated ?datatype;");
database->execute("ALTER TABLE listings ADD COLUMN date TEXT;"); // date when first listed // will use ISO8601 string format as follows: YYYY-MM-DD HH:MM:SS.SSS
database->execute("ALTER TABLE listings ADD COLUMN date TEXT DEFAULT CURRENT_TIMESTAMP;"); // date when first listed // will use ISO8601 string format as follows: YYYY-MM-DD HH:MM:SS.SSS
//database->execute("");
// For most recent listings: "SELECT * FROM listings ORDER BY date DESC;"
}
@ -198,31 +199,31 @@ void neroshop::Backend::initializeDatabase() {
database->execute("ALTER TABLE subcategories ADD COLUMN description TEXT;");
// categories types
int category_id = 0;
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Food & Beverages', 'Grocery', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAAx0lEQVRIie2UMQ7CMBAE7yAlTaCFP/Al+EDgW7SIgm9AhygpIySoSDU0ETJW7JwRKYLYJvJlz+ONY4v8lSJgSbsWXg8AofHAY4wN67B4Xvo9wMTQY/EEAf3/RLkVAMwiv+ctBEjZg2vEs2+sApXhoD1qbx54XwLTUIKzIcGpfo5EpHTqdxHZiMhcVS+hBGtDgsLrebsaogIyYBeZfAtkHwMcyAo4OhMfgAIYNvjTAKnNbR5/k7+uzgGZX2iK69ZUVVMAnSfov56KfCz1jwSA8gAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Food & Beverages', 'Grocery', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAACZUlEQVR4nO2aPWgUQRSABy0iokRIdWoTBJtoIVqEgD+NINHCZiWFprCykYDa2MUmimBjCrWyCAouBCwEC7GwUUyTwlILrSL4V0RBRf3CoMKRm73d2503vovvg+lm3sz72Jmdt3fOGYZhGIZhdAEYR47limvIQoML+uaBrnmVeYIAJ5FlQLuAKWEBLe0CpoUFjGgXMCssYL92AXeEBRzXLuChsIDT2gU8FxZwQbuAV8ICZrQL+Cgs4KZaAcB64KewgFyzgCHkeaxZwE75/FnULGBUPn/eaBYwLp8/nzULOEUaBrQKmCINLa0CLpGGEa0CrpOGjooQGP6TuG/XCsZlgfYs0O+e1kqwsCIEJojHrNZKsLAiBA4Tj4t1BSyQho6KENgbMf5YXQEvScNMwRkQg7fAuroCPpCGW4G5t0SKfb5W8h5/SyMNt90qgM0R4voneIOrC/CJNHR8EwAGG8ZcBnbXTt4DvCYNV90qgG0N4r0HDrqmAPOk4URg7kM1Yz0BdjRO3gOcQ55fwHYXvgj9qBjjC3AfOOZiArSAb8ICHnWZ358DY8CVgrFHgF3AxqiJtwPMCQs46kropRiKDrBV8Mvwg4pr+HcCPMCkQPLvvFzXDwI8/kYVMXl/v9jnKqJCgAc4C3xtmLz/pWmP6wE1Ajz+dlXw0aGM78ANYJPrEVUC/gIcAO4CSyXv+BfA5dC7vq8FtAOcKRAw5CLQDwIyyQWaAOwJyGwLCG6xxtgZgB2C2Zo+BPn9P4G8S3saWmDJmLyfBGQI0HR+2azbMAHYE5D971tgtOxAq9Oazi+btWEYhlvbrADFelwIpcui8wAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Electronics', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAACO0lEQVR4nO2bTU7DMBCF3yDgFGVBWSNY0ntQDsJBABXBHWgP0u6Q+FkCC3qKIvpYpEShHbeJk9gpmW8VWZN4ZuJxZmwHMNqNVPUgkh0AvZUOREaKbA9AZ6l5KiJjRbavdDcWkamvrrVAsk8Fh+xQER06ZDU0p3ixU9WDthVzQGwFYtN6BxiGYbSa0qkwyRMAfQCHAPZKa5SPLwAfAB5E5DlQn38huUvyjuTcka6GYE7yluRuDAfcRTR8mYGvHV4hwGTYP/reXwNzAKc+4eCbCZ6jOcYDiR3nPjemscMC9TyArk9nNXOkNXLDekJ28ugB0Gpy7U2Hmu2LsO9o12y6ADACrBgyB5gDYisQm3QSXMz2Tfq0VYKIrLXJRkDAvqYAVtb9HWj7BrUQ0gFjEbnII8hkj6Cytf91WAj8XpA8A3C5LJD3rTUV6jtOVyIyAf6GwAECDbvAaDaNAEwACwFzQOsdkJ0DPrEoEf8Zmk2fvxfZVHiCpE7+V2z6ilkIBOyr5/gmq7K1apIhpAM6aGCe0foQSB3AAoectgnHRko6Em0ExFYgNuaA2ArEJvsZHCN/JvhVgy5lmTnaNZvSpblsKjxF/lrgPb9ewXjTGh17mym+ITBCsiXdFObwLOS8HCAiTwDufe6tiVsReQnaI5MjMgOS31Uf9yjAN8kbljgiU8UhqWMkOX4X7i3qqpkhmYceROQ1UJ+GYRiGE9pPU9uJOSC2ArFpvQOqXBYvsp5wjdXqzfUr7Np63jDK8QNj5L+yJWBCUgAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Home, Furniture & Appliances', 'Domestic Goods;Furniture;Home Appliances', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABEklEQVRIie2UMU4CQRSG/0dgo0fAwoQYW0NDLLwAFVraeB6xNqGwgGsQDmDiIYgBZfUGmih+FuwGd5zAzkCMBX+yxbx9872Z/72MtNO/E5AAN0BKec2ALpCUKdANALu6dnnmKZBKqkca8GJmB+sKEAlfAM0KzIonZ7YB/8kN+AqcSkoj4a1fUeACeAWmwFkWu4po8GW2tw08A2Ogo2yRawLUsi9kTFOg6tk3rUiq/bjQoaSOmX1IGgTY0zezT0nnKk5gIqDnnGYYZn3B7pHDuhVw4gS/gKMIeAOYO6xm/vMhwO+yupeWY3oXa8sKLZnAPvC4xdOPgT3Xw2MWTXrfAPwGDFnXwzy7rBer8n1PxU5/q2+5j5kIl7IVAwAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Home, Furniture & Appliances', 'Domestic Goods;Furniture;Home Appliances', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAACoUlEQVR4nO2Yy2oUQRSGKwkqibeFF0SjuPC+EQQvuPJGEEEGldmKlxAUl6KQjQhufAEvoIIuTGBwo75BXAgRdRtcqJgH0BiiRoVPjvSQoadrpipOV1W39UHv6tR/6lD9V9VRKhKJRCKRSCQSmRdAH7APqABVR18l0exVvgC2AqPADP4Q7RFgi+vFDwKzhIPkct7V4s8SLkMutv0s4SK5bc6zAKOEz+M83X6G8JEc+/IowF6Kw548ClChOFTyKECV4lCNBeg0xB3ACf5zD+gGrgI/CZdvwAWgq+MFqAOcDLQIcv4fUS4AhgiPU51YWC9wEDgKbGgz9inh8LBNrpuAY8B+YHHWgEXATWAqNfEYsEMz6TbgN/6R33Fji9vry9T4L8ANYGGjuT1vIfAZ2KkRqOGfEU1uh9u8XWQHd8vAMwYiE8CCDJFD+OeA5lf+aBB7Wga/MBQ6lyHUBbzDHxNZR15yFJowJoOnDQe/0my1K/jjsianN4bxUzL4u4XgrgyxVcAP3COaKzPy2W0xx7QEvLUIuBtQp0hnfvcs5hiXgGsWAV+BJZrjpub4a2p6AMssfmlhWIL6Lc/zQRUowEWLdfwC1tYDn1kEjqtAAV5brONJY+Bx7GgyQ99Ymp8w0BjcY3hxqHNbBYal+b3/ewtsBLhuaYZLVSCIMSc5mTKcNUl/II+bvJkzvzRtHkVlYc780szDDIvIQKsC9FiaYdFoNr80lmZYNJrNT2OGITY9O/F4WqNMAO5TPm4ZLV4AVgCfKNe/v1zZAGwvSRE+SFfYavF1gNXAo4JekOTC8yCraWINsB64JA2R5C0+mSE46bAX0Er/TvIsXvfPC9ehaYfXlCN86yvfCfjWV74T8K2vfCfgW1/5TsC3fiQSiUQiEVUK/gB5u4ODDC6IPAAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Patio & Garden', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAD20lEQVR4nO2aT6gVVRzHv7+UMk3RAjErynTRoiBCqAdtcmEiUiFkqxbRrnbiQiLquVKTstxIGOTGzQvJlegiwUUlRJYP+iMYQaWP0pSnkvi093Ex8+h6PWfuPTPnzNyb84EL78098/v9vt8798y5vzNSS0tLS0tLy+2KNV1AJ8AcSaslPSfpKUmPSrpX0lxJFyVNSBqXdEzSUTP7tqFS4wIsB3YDk4TxPfAGcHfTGkoBLAB2AtcChXfzK7C+aT1BACuBXyoK72YMmNe0tp4ALwH/RBY/w3HgwX5rqX0SBF6UtF/SrIJhVyV9Iem4pL8knZW0UNmkOJK/is4/KelZMzsXo+ZokF32RZ/8b8BrwPwecRYD7wDnC2J9zSBNjsB84JSn2GlgKzA3MOYS4ECBCe+n0hMMsMNT5BTwaoW4BmzzxL4OPB1TR9kiH86FunglUo6PPPG/jBG/anG7PMXtjphjNvCVJ88zsfKUKewu3JPVBJEnKeBJ4F9Hrn0x84QWtdbzqbyVKN/njlzngaLbZjqA9xwFTQGLEuV7wWP4Stf4O1IU0YUr8biZXUiU74ik647jzrtBHQYscxw7liqZmV2W9LPjraWu8XUY8Ijj2Juey7QMZ4DNXfH/cORc4iouyADgceAg8BPwIXBPyPmJuF/SVuDljmNTjnGzXSc7D7ogu5celrQgP/RY/lrTb4zErJX0Wf6367fERddJfV0BwIhuFj8TcEtAganp7AOscLzvnHR7XgG5+EO6VfzzZpZsMisLsEzSQ463TrjGFxrgES9JYwHiN/Q5riozE58v3zdB0YAHKP69PVqp3AQAd5L1B7tx3RZ7Btvcxy1ooEwANnnqDF92dxkwCawHLoea0IeJsTiBu9t0lYAeYWfhS8m+ApNkc4GAdz3JvSYkkRrGB8HiO4ofIRef/z8POO1J5DShNpluzgH3lTbAI+j1goTbBsiAabIOdFyAWcB4QeLRrvFNccuHEdOEVf0mr0Opg4+BtHsewKEeRYw2YMBMez39hg/wBFnbuYjR5JL/429gXXLhXSbsqVFgL8bKaKjaEHlb0qWKMRqlkgFm9qek8guNASBGS2y7pN8jxGmEygaY2RUNVmMkiFhN0U8lfRcpVq1EMcDMpiVtihGrbqK1xc3siLK+4VARe19go9y7MgNLVAPM7EdJe2PGTE2KnaGhWhxFN2DYFkep9ga3y70/N3AkMSBfHA1Ux9hHyt3hoVgcJTNgWBZHSZ8PyBdHEylzdHC6zEl1PCCxq4YcVyR9UubEWh6WBjYoe44gxQMVZyTtMbMfEsRuaWlp+X9zA8EbEeUfOYl4AAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Digital Goods', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAAoklEQVRIie2TUQ6CMBAFV2N6CG8i90P0ni7XGH/Wn1oeS0piTJivLm2YBDpmB1shiHUBnsAMODABpT7XmrOCB9/c9xR4jDdgiLXvKVh8SVZwXjV2clnZn83sCgxmdopn3mWsPsPU+Mljfa41ZwUlJA68gJHea/r/oOtNl60Eqt502Uqg6k2XrQSLt2PL3oeflKzq7S8bXW+6bCVQ9abLPkjzBiW5R8M25vEUAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Digital Goods', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAADS0lEQVR4nO2au2sUURSHJ7Exia9SRURJRA2oENFNQLcUFXQ3ypYSNBZaqcFCUCSinfgfmMZkjaQSQTvFJ9qorYiFj8JnhEACRtFPDrmRzc25szNj2Mks94Ot5t7fnPkx9+ycc28QeDwej8fj8VQBKKEQMr4BKAIjwHtgEvgEPAT6gMUhc2WOzYhjrEYpSNMAYC3wjHC+AN11ZwCw0TxcFP4Ax+vGAKAFeEM8fgH5ejHgHMl4ITkj0wYwlfQ+OF7zIeAIcBEYcwTdmXUD2h3B9Fs6HcBPZdz5rBuwxxHMckXrsTJuIOsGlMKuW1qhD+gNwL8BI34JzMbngGCeJ8GCI5gWReuOMm4o60kw5whmRsEDLANGlXGXs25AEzCuDJESuBtYAmwB7juCPpBpAwTzyZuEb5VLJcsGtAE/EhhwwtLJpgECcCjmw98EGoN6MUAAeiK+CdeAhYFF5g0QgFZgUEmMv4EnwN7AwXw1YJUxYcYvwjz5d9gO7Ad2atWhMqdLuVeXY+ysmCTWpM/p8Xg8sTBJrtMUQ3lgxX9orTQaBVNbNEWYk04SBNYBZWBC6QI/BfbF0CqYnSSZW8mE+SttC5lbm7/BSoDDZq+vGmXtQ8d6e4Yj6MhHVc+8MICpr7w4SO2/QNFpBG7F1DqaqgFMvfZJip1TitbpBDpy79Y0DSiTjFGr3F0EfE+oNZiKAUCzkvCmGx5F0/DYDNxzBHWwWl0B3AU2Ga2i0bYZr/x3qKUBOcfNita4pabBEdbyuqJc/ypzLS3pJGlsS8OAwhw2PbWldFvRke12jUIaBpS0O6W99eUN0PFvQOCXwByDzwHsjnEAQnp+Nlcrrg8o1x85qkONXWnkgHbHzS5Y47aak19hR2D6letybKbD0pIzRRob0jCgwZz41LgO9AKXQg5B5aymp8aY0egNqRLfWifKamOAAJwlGc+toKUSfJlQ68y0ThoGNAOvYwYsy2GHopV3LJUwXtldopoaIMj6Az5HDFg2QY4FDuSYrNIFciGF0XpFo7YGCMAaR6a3A/73ze7CFDzVDJVjdasd82tvQEVSlN2eG8A70yL7aPb/T0rNH0REjs5L0wR4YIybNMluuFpvMTUDPB6Px+PxBFnnL5kjkrLcntRPAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Services', 'Non-product services, Freelancing, etc.', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAADjUlEQVR4nO2aS6hNURjH/+t61c2juCjKsy5loCiuiTKiFGUiTEQxo0gGCteEiYmZPGbUjQkhRhQDEiZInYG8out6XFeKe/kZ7H1qO/fsx7l7rb3Ocfdvck77nG+t7/+tb639rbW3VFJSUjKKMb4dkCRgk6T1kiqSThljfnh2qTiALfzLZd8+FQpwrSYAQ8Ak3345B5gHHAR6Gc5pYLVvH50ATAPOA4N1hNfyAOjy7XMugA6gM/y+AnifQXiUQeBAaD8DWOhXUQMAe4CfoZDnwECD4qM8BP6E368DE3zrS4Qg1X/lEJzGDht+ttloJIapksY5bH+GjUacBcAYU5F0N+PfK5KuSHog6XeG/w9Iav56AZgC3ElI4wFgZ41NJ/AkwaYfWO5LU8MANxudx8Bs4HOMzRAwpWgdIwZ4FyOkkmJ3PCFwq2z553IRrNIRc/15it2zhN+mj9CXYRQRgO8x19NEzEz4bWCEvhQPQQETN5c7Y2zagEcJU2CuLf+KyIC4VB8jqQeYE70ItEk6KWlZjN0XSR/suecQoDthFKt8DRe8bcD+lJGvch+Y6FtfIsDMMM1dsduGny6nQLuCNHfFZBuNuCyFX0q6HrlEziaj9p8k9eRsT5LjQ1GCLetWBbe0G5I2Sjo2gqbuStouaZ2kSZIuGmPe2PKzUIANZD8UGQROAON9+20VoB3Yy7+HHFHeAqeABb59dQ71d4vWSt00iiiE0qg9M3hhjPnoxRMfhNPhAsE+/yGw1LdPLQ2wBLgC9AGvgLNA3I70/wLoIiita3kKtNezsV4HhPf+TkmzZKFaM8Zcythvl6RbCX3uMsacyetPkgNrgMvkO/sfRsa+u8I1JInT9WzHWhA+S9I5BVVa4WQY+Sp9LjpfTlC0uOJnSv+rMow8BMXWStvis6RdXmLPDckuHuBQK4oH6LYg/miriq8Aw+Y1nsU30nkeHgHzixCfuQ4geBhxU/nv7b2S7qn+M8D3km5LumqM+ZOj/25jjL3RbzDyaSxy3H/Tp/3iZhKfOAWwl/ZRPirYAmd5DC5JazP239Rp75oj1oSPNvFxJ0K/lD1FfXLUGFO3WMoNQZ0f95JCM2A37VssCO7FR4LQbOvB4cLEN1kQXgObbWtzUQp/k7Qv/LTBkKQ3kh7XlseFQ/qa0E+rv9ycRkIQ/n/xVeoEYfSIrxIJwugTX4Xg7mDtpcWSkpKSkoL5C2IZ98cgmOE6AAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Books', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAACCklEQVR4nO2bPWsUURhGz00iWlgYFAtbQRFUgiCI2NjZJgEbSZlOsNTKP2EnNmkCaZLCzkL8HYpg4QeSEAJiEVEeC7dY1M3c3XnnPpPde6plZ/flzNll7sDehcpskyZ5k6Ql4CpwHjgRahTPTkrp7aiDC7lTJCVgDXgKXAwQK8U7oF0ASaeADeB+kFRvmMt83Qum8OQhI4CkZeBBARcLOd+AJ51bGDkygKQLwM1CLhaavgGXmHCpPC40BVgsYmGkKcBUf/owxo1QBu+Bg8B5TSwA18hfykcOieAbcDml9CtoXhaStoHlNjNa1RvisPTJD/jedkBUgGNLDeAWcFMDBM1x3S+09o9aBs9Keg7sB83LYR6413ZI5I3QeuCsYtRrgFvATQ3gFnBTA7gF3NQAbgE3NYBbwE0N4BZwUwO4BdzUAG4BNzWAW8BNDeAWcFMDuAXc1ABuATc1gFvAzcwHaPPb4E/+7A3qOz8mfqekFf3LlqTbkuYDJfvJfwI8dDsV5a8AL90+XTDORXCzMwsjTQEOhx7vdSnioinAx6HH17sU6SWS5iR9HlwDPkg67XYqjqTHQxfCV5LOuJ0iadzeJukk8Aa4NXjqC/AMeA3sdqcWxteU0sg9xVn7+ySdA3aAO1FWBVlNKW2POpi1DKaU9oC7wCPgU5BYLxh7h+fgH6Q3gCtMwV9nK7POb7zRpspIHJDQAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Movies & TV Shows', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAC7ElEQVR4nO2az2+MQRjHv09pohHclBIkUhc/Dhqk6iYSN0LqVxxxpPEPkHB0QURE4w/g5CROIkSJgyoJ4kaiKRJpKyG0/Ti8u0mzZrbv7nR3urvzOT7vPLPf7zM7M/vujJRINAyAAT2xdUShYP4GMAUci62nrswyX2QKOB5bVy6AbuBSQL4Bt/ifP8Ch+dQ67wAngYmC4DNV5PvMFxkBFtdCexBAB3C1ROwvYFsFfZR+7Ut5D3TV0kfVAIMe0W+Ajhz5c438O2BVPbxUBdAJjHrE35wjt3FHfjbAfmDGY8K5jTWN+SLAFY+RH8CGkrbNZV6SgHZgyGPoBdBeaNd85osAG4Fxj7HLTW2+CHDCY24aeFDG/MJe7SsBuFPGaLSRt5ziV0g6LemgpG5JK2spKoAxSR8l3Zd028zGg3sEjgLfKxy9hcA3oD/U/Dn8e3kjMAOcLefROwWAvZIeSloUVMX4TEvaZ2aPXA+dBQDaJA1L2lpDYfXktaTtZjZT+sBXgD2SnjgeTUq6JumtssrWg9XKFuAtgf30mdmzXC2Bi4759BfYESiiKoAlZG+SIVxw9d3m+cz1jthzM3s5f7byY2a/JQ0GdrPOFfQVYKkjNhooIJQvrqA5kHTP0XSZK99XgJYhFSC2gNi0fAEW3l/KFQLcdYR78+Y3fAEkBb3wtPwUSAWILSA2zbAGuH719Upamye54QtgZkdKY4WdIdfi2PJTIBUgtoDYtHwBGn4RBAjJb/lvQCpAbAGxSQWILSA2qQCxBcTGV4BJRyz2NZU1gfkTrqCvAJ8dsV3AzkARVUF2ufJUYDefXEHf4WifpKeORz8lXZc0ovodjnYpOxzdHNjPbjMbKg2WOx5/JSn33d4FzrCkHtfxuHMKFBoOqH6jXEumJQ24zEtldoHCjYrzkoJeNiKDMvOPq+8B+skuHDUaX4HDc/nLe01uubKF6ICkTZI6q65obRmT9EHZNblBM3NufYlEIpFIJBKS9A+F5c3zqqADtAAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Music & Vinyl', 'Musical instruments', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAFHUlEQVR4nO2bXYhVVRTHfzuiJBuNMc2ERDJ0irEilF4sDaIPeopAjaCnihF6k0CUEqLGeQoVfEh7CsqH8CnNqBCCCgqJzI9yCMf8wjKFGY0+EH897Gtex3POPV/3XsH5w8DM3mevtf777LP2WmvvgQlMYAITuI4ROqVInQH0A/OB2UAvcCsgcB44AxwDhoG9IYQznbKtLVBvUJeqm9T9FsPFxpgN6hK1Yy+qMtRe9Q3114KkszCirlF7u80vFeoUdUgdq5H4eIypg+qUbvO9Aurz6sk2Eh+PE+qybvNG7VE/7CDx8diu3tYt8vPV4S6Sv4RD6txOk1+o/tZl4s04pT5Uhkvh7UVdBOwm7uHXEsaAx0II3xcZVGgC1HuAr4A7iozrIE4Di0MIw3kH3JD3QbUH2En3ye8EhlL6pgMfN2ytF+q2bn7kDexQb27YM6BeSHluW93kV3SMYjr+J99k10DG8/XECcYI70RHKKbjKvJN9q1PGXPCHBFjHh+wBphVdOJqRi8wdXxjY1IWpIyZBayupNWY2LQzti+CEbWvmbxxZWRhzBYJ1I0t5uBVoG6POgrsAg4AxxttdwEPAE9k6JsD7FIfbsjYDjzTQlcPsBJ4u7CVxnz+SI1v8LD6gnpThs6p6ir1aIacb2z95psxYpl6grGYURc2m0E8Qfdk9d0a9S8pMwGbalK+qrDyyzasrcmGDWWUFy1jJWFzWfJNdmypwY79afITvw1jAfNUWn9OjAB9IYR/K8hAnQz8RHSUpcUA05MKrWlxQD/VK8avVyUPEEL4E9hYVQxwf1JH2gTMr6hwFPioooxmvAecqyijL6kxbQLmVFT2aau3ry5QNzZ++rOeDSGMAp9VtGl2UmNaIFS16rovq1NdAHwHTGo0vaIuCiGkOivgB+C5CjZdFUpD+gqoGv2dbNH/MpfJ0/j9pYoyWyGRU+6CSEG0cqDmbCsisxTSJmCsotxW2eNW4O+mv/8iOros3FnJonj+eBXSfMBoRWUPZnWGEPYbi6uXlv3WEMKBFjITt7ECyM9JXVkx8hpTE51OGRhT36pp+UCS7LRP4OeKNvfQ2qkVwXKqO+ZDSY1pofA0Yom5iuM5BtzbiORKQ51END5xH88rBpgZQvh9fEfiCmjEzAcrKIQYu79TUQbAJqqRBziYRB6yt8EvKiqFGOCsLTtYXU2MGapidxnlSyo6nWZsMWZ1eXVPUrfWqH9pmQkIxnJSXuwwlqvScNRY7krdHYze/kXrvV1yRE1d6ZlOzrh838oxXzuJcfpU4Fuyk6lzxMRmL9FRBqK/6Aeeov4i7LoQwpulRpqvLH7FoYXaZ7GV0060LIvnmYTBDAWJJzbqDLM/h05hsBW/PMnQEOmZ2L4Qwj8J7aPA2Ryy24mTwPpaJKnLMmZ5YNyzeU5sOoEVtZBvIpZ2PH7BxiR47ZDPfTyeO9Q1XjrYA8xLeWSIeFDZ6riq3RgGFoYQctUQi16RuRv4GphZwrBO4A/gkRBC7mSuUEUohHCY+IarFkzagXPA00XIQ4mSWOMW1mKq1+jqxFngyRDCno5pVOcaLyl2G4fUNL/U9knoUT/oIvn31e7fVzTGCZ28R3RcXd5t3lfAeJlq0PZeqTmvrlNv6TbfVBgTqDXWmxD9or6m3l63vW37VxTjtZRHgWeBx4H7CugT+BH4HPgE+DKEcLEddnbyn6amES9CzSPm/9O4fOH6PHErO0qM5PaFEE53yrYJTGACE7hu8R+yRA2BiW4OUQAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Apparel', 'Clothing, Shoes and Accessories; Jewelry and Watches; Fashion', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAA2UlEQVRIie2SsQrCMBRFX/wBEb9CF/0YoYO6qrMODhZ09Dv003TQyQ5uRo7LK1RpJTXaweZAoEm494RHRQK1BegDe+AI3HHnrpkd0CsrXQC2hKwIC8xdpZs3RVegk5Pp6l0RGx9pygkYAW1dY+DskMuXO0p92b5K4wqkKbGIiFFxIiJNp5/An8QY02roZioitgKpFZHZ0wkwAG6Zkay/MNZl5tsCw9znABFwAVa690I7Yu2Msi7zbi5p+FOMMYX9jaKLXxPEQRzE/y8+enQffMSTD+UHzQZqzAOhndP5TQOzlgAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Apparel', 'Clothing, Shoes and Accessories; Jewelry and Watches; Fashion', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhklEQVR4nO3av0rDUBSA8dBF7DP4B1wERxF8kD6HQlEonZrRVxDBTaFdXH0NDQ5uuluJ0k30kwsJ1JDatMk9p17Pbyq0cO7HTW5LSRQZY4wx5s8BtoFj4BZ4BCb4M8lmuFlHwJZk6CZwDnyg5xMYATu+Yzued3JRbi0dX7F94IvV49bUbzo2XnIxD8D6AnPWgLslZ51px+YugFaFOS3gknpi7djcFdD+ZU4buKYZsXZs7gk4AfbcZZ5FutenwDPNirVjNVS7pwOJrRZNWLG58migS7i6ZcFjwjUuC04IV1IWfACkhCd1bbPu4/3ALu0UOJx3UleJHqCvVzu2YvQg+4yqbA292rFzogdT76uaWkevdmzhIEuy8B/fZSgr+Q3xCtzPPKDqWqVgEViwLNth31DmPbDIgoVF0lBmwb5hOyzL+44WCfdZcCQN22FZtsO+ocx7YJEFC4uk8Q+D3xV7U43gG8XgoUbwbvbXqLQXYEM8eOqpPPdE3JtAqJsxVIs1xhhjTFTXN9+cDWs9QZG1AAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Pets', 'Domesticated Animals', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAEcUlEQVR4nO2a3YsVZRzHv4/HsjfbLLctaMNqgzapC7Oydwgh0kuTqLvQiqD6A4reichujW6KbmqXrbQwNuyii7rpwlXXtDKLCs1Aio1W18yV/XQxM3Ac5zzzO8955pyF5gPD4Tzzez9nnteRampqampqamoiAThgAzAJnAD2AI8BC9qwsQB4PNU9AXwDbARclbFHAdhEMe9ZigA0gJEWNjZ1I4dggHtaBJ7xjMHGcx79OeDubuQSBDBeUoDjwBUe/UHgnxIbn3YzJzPAecBsSfAAL3hsvGTQPwmc283cTAC3GoIH+M5j4wejjZWx4jb3zAYuNcpdB1ycb0zbrjXa6DdHVULMAlhtOUnDBe3D6T0LC41ypcQswJ9tyJ7xD5C0tA39qTZkvcQswPeSMMqeKmibM+oiqWU/0i7RCuCcm5J0wCj+R0HbEaPufufcX0bZUmL+AyRpzCBzUtK+gvZ9kmYj+egNJBOZ4yVD2DaPfkcTqUpJk3seGAXeAG5pIfesJ4E54C6Pj6CpNLAqjWkkjXEwVt6ZgzXA0YKA3gHOzsk2SBY+Rbxm8GVeTAGLgHcLZKeB+2MlP9gi+YwPyS1TSZazGzl9SfyI0V+2nM6WwrvT7/nkHbDVE9dRYjwunl+kmQ0dO2o/rkcNcb0ew9G3BkeHgGizM0NMC4HfDHHtjeGsrFfPWN2m3QHgvvQaaFN3tTGmY2W2LPMA6wztTosQsAz4SNJhSdvT6zDwAXCl0Zd1U6Q0dksBDhqdlQ49wB2SJiStk9RoutWQtF7SBHBbDF8ppbFbCvCl0ZnXFnCDpHFJl3jE+iV9BizvxFcTX5UJWAy9Jdsi5+dWN0h2cMYk9RnsXCRpDDgnxFezWyWxdw6wzdDh3OTRf9nYaTXzosfeSoP+1ijJpw6XAj95nG336F4OzAQU4Bie0QH43KN7APA9akFFGCI5oMizC2i5RQW8GpB8xiseu/0ks8Q8k8A11rzaOmkhmew8IOnetOlrSSPOuX9byPcpeV6LdoAsTEm62jn3dwv7iyQ9LCkbOb6QtMU5V7Th0n0Ie/bztOwL5jUkz/50hAJMA5dVFWfsHSFJEnCWpFFJiyOYWyxpC7ll97wGeDPCL59nc6/zKoVkM2RzBclnvE0XV51tAfQBn1SYfMbHJKPL/AFYCxzsQvIZvwPrep23gCXA+11MPM8IsKRXyS8n2QnqNYeA60PzCHrnBrhA0m5JQ6GOI/OjpBXOudIdoDyh84CHNH+Sl5Jj9QdDFEMLsCpQr0osO0lnEFqARrlI1wmKKbQAewL1qmQyRCm0ExxQ0vHEmOvHYFrSkHOu6NjdS9A/wDl3RNLTIboV8VRI8h0DPInt1biqmAWe6HriuSLcDkz0IPkd2M4QqofkpHY9yf5g1exMfUV5cTr629fAsJJ9ujWSblTnr7SdUjLqjEsadc7t79DeaVT6+jnJlPlmSSskXZVeyySdL+nC9FOSZpT05DOSfpH0a/q5U9IO59xMlXHW1NTU1NTU/D/5D8Q7H3ts84ozAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Toys & Games', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAEAklEQVR4nO2aS2hdRRyHv0lrsb7ahQk2ol1119YWQaUgCq31QYUq1ErBR0QFBSm4EcWF4EY3KiotIhUfuyAUBB/YjeJCSx+SpO1CUYwLH6W2xi5iG5PPxbmhye3MfZx7bk6Se7/NJTP/md9/frlnZu6ZgS5dunTpYELehuo64PHKn++GEI7ViH0Q2AL8DLwVQjibV3deoK5Tx73AuLo2EfuCszmoLp3rnAtFfdOLeSMR+1skdtNc55yiJ2e7ayJluyMDFVgVib02p27h5DVg0ZDXgKkWdSdbbF8YeQ0YbkFTYKSF9uWjXlmZzZtlSn2p7Pxn0so+YClwM9BfKboFeDYS+kDlcxIYCSH8mFdzXqPuSKwC85qOXwUaegTUFcATwHZgDdCXU+8U8BPwDbAf+DaEML+/JepO9VSOCa8RflCfUpeXPc4o6u7KzN1uflHvK3u8s1A3q//NweBn8rm6puyxo/aow3M8+GnOqtvLNuDWkgY/zZT6ipp7n9IoqWVwc7uF6xCA54B96pJ2CqUMWN1O0SYYAPbbxlUiZcDl7RLMwb3AZ+pV7eh8oewEbwcOqLGXKy2xUAwAuAkYUrcV2elCMgCgF/jE7J1kbxEdLjQDIFshngFG1b3qFvWyVjq7CHUQ2JG30xI4DxwGvgY+DiEcbbThQvwGxFgGbAKeB46oX6k3NtJwsRhQzW3AQfXFervJxWoAwBLgZeDtWkGL2YBpnlafTFV2ggEAr6pXxCo6xYCVwP2xik4xAGBjrLCTDOiPFXaSAdHlsJMMiNI1oOwEyqZrQNkJlE3HG5DnutoYsAc4RnZZahfQ0E/PHAwDHwB/AGvJDmivbpPWBdTBxIHFiHpdVWwwfm2uVfaol1Rp9VVyyMNgqwZMqDck4pepQzkTizFk1eBnaG0035ll1IBm5oBDIYShWEUI4TzZV7Uo3g8hTCS0vid7/VUIzRjwd536M60k0mRfp4sSSk2C/0TK1qs9IYTUHcHY4zEGfFknh63AiqqyDalgtQdYH6k6DpyoofNdnTxmiexMPEePJuL71bFGn7uqtrH5ZszEKZA6kMituLfYaq/6b0RkXH248l+Yjl2vnkgkNdCA1mOJtsfNruRPx/Woj9TIq9jlUX0vkZjqqPqpelSdTMT8qV7agM5y9WSij0n1SEXr1xr57Ct08JXEVql/1RCtx64mtB5qQeeM2p7b5+o96rkcSe3NofVODp1z6t3tGPvMxLZVXG6EKfU1Z8wRTej0qK/b+K200xZ8UlwrudXqR9begR1Stxagdad6uIbOhPqhen0RY2vqEpLZ0nQX2TrcR7Y5GgUOVHZohWB2nLUBuIPsus5K4CTZj6MvQgi/F6XVpUuXLh3N/4LZQfQlM+IfAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Baby', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAD9UlEQVR4nO2bS4gVRxSG//IxzvhYZaNGxpnJJghCBBdCdi4iIRsNqLjQvYgEAllkJbjwAQkJqItsgskQJIIJYjDim8EgQUEQjeAwhBkFRUVdRGKI5nNR3Vpz0923H9WPq/0t+9Y9db7TVX2rq/tKLS0tLW8wpu4EfAEYSR9J+kDSW5ImJB0yxtyoNbEqABYBv/F/ngG7gRl151gawBJgPELeZXfdeeYG2ARMAA+APcAc57PBFPLhSHi3To9cAJ8A/3XIXAc+B/YCj1LIh+yI6qPWiyD2wrVC0rCkxZKQdEfSJUnrJH0lfzmOGmO2dB6c5Sl4JoBhSZ9JWitpUUXdPoo6WGkBgH5JuyRtk9RXZd+STlTc33SAxcDvGeasT8aC6Vab/AgwWZP8OLCwTvmlwJ81yd8EliTlV+qwAEYknZM0WGY/MTyUtMIYM5XUqLQlYs3yknSgm7xUUgEaIC9Jd9M08l4A7G983fKStJkUN0FeCxDIn1f98pK0StLWbo28FaBh8iGruzXwUoAGDftOrnVrUPhn0JFfWjSWZ65Ket8Y81dSo0IjABiUdEbNk78haU03eanACACGZOd80+SfSBo2xtxP0zjXCAjO/Fk1T16S5knqT9s4cwGCMz8mu4nRVFambZipAIF8VRe8xwW+m3qTJXUBHPmhPBll5EdJI5Iu5Py+3xUudvd1oqJb2J+B2UG/c4FTOWJ87FN+iOru548BfR39DwC/Zozzni/5KjczbgEDMXnMAY6mjHMbH9tfQD9wpSTZOL4h5g4OmA0cThFjf2H5oMOdparG8x0wMyanmcBownefApl+oeKqvUDS9hx1y8pPkkY7jm2R9APBhdDFGPNc0r2EePuMMZOFswLWlX+iOYId1jOAgxGf/4J9juDmtSsh3kWc54ZFC7DHu+50juCcYezQ/j6i3XGCCyPwRUK8ScDfEybgW+/Kr5gmn6IIp4GvE+JNYfcg/QHs9+/9kjFgfky/cdMhjingHa/yQSKflmHucAaYG9N33EjoxP+Zd5JY5ln4n4hj3YrwRy3yThJ51uBR3ASGsWv8TiKnA8lX+3KGfUQSy4EnBeXHgbeDeH1EL2enjQTgy9rlnWQ2YN+vKSTvxEssQqPknaQ35yjCU+xucVS8uCLcapy8k/RG4N+MRThOzMoMuwKMuiY0T95Juo4iNEM+hHzT4SgdmxxOvG4rvObIh+BpJGBfYe0t+ZCiRehp+RDyT4feG/ZxkG8kvB7yIZ6K0JvyIQWL0NvyITmL8HrIhwDrSX8DdZmY5XJPg72LPJkgfh/7vn/VL0tHUtqboth/aHwo+xh9QPZ/ABclnTfG/F1Wvy0tLS0tGXgBtI9sAulPU94AAAAASUVORK5CYII=') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Arts, Crafts, Sewing & Party Supplies', 'DIY & Handmade', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABaklEQVRIie2Vvy9DURzFz1f8WNRQNRBdWCwSE4vJRpgkBiwSi8TqD7FIRVgsNv4ANquFQYhJmwaJDg2DpvWx3Cqv13u3tYmTvNz37vfcc873m7z3pD8HYA7Iu2s2gL8IXACboQZ5GrhP4HYCz8AWUPJxOjx79lUjIc+4pLSkZUkPoQYbkh7d/U2CwaRbJyTlErgNAFmgCtSAbAzvwI2yDPT5OL4OZGZ5SWeuvhqTZcqt+2ZWDsz/mW7JpbsFzFNPuQ5rwGhL4k6gG3hyJtOe+oyrncTpeEckSWZWkXToHtc8lPp4tkNDNwEYA96BFyAVqR0DV77xtWpyDlSAhch+AVj/lbgTygBdnv0BoCfpfGx7wLCkFUnzkkYkDUl6lVSUdCfpVNKRmRVbTZ0CdoA3klEFckBvqHgauAwQjuIayIQY7LYhXsdeVM/3hhYlDQa124ySmfW3ebY9+DpI+gfEC5p90/zxU/GPOj4A/ViPv/A3dWcAAAAASUVORK5CYII=') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Arts, Crafts, Sewing & Party Supplies', 'DIY & Handmade', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAADZ0lEQVR4nO2aSWgUQRSGyxh3AwETxA2CuIBIiIoyQXDBCCGgKCQXRTEXEdccFA/iIXqIS+LFgzuCKIpiwIsQEAUJKBrNQcQFBBUVRUUFFfdPKtOEztg10z1d1VNt+oM6TA31/qrX1bW810IkJCQkJAQDKAM2ASedshEYJSICGAZUOaU4Kt0egAbgE//yEagXhgFWA+9cuq+A+aZ1ewAWA79QI/9bJAwBLAf+eOi+Nz4DgQHAA3Jz35D+QOCpS+c58N31e4MJ3V6ASvwzXWgGqHXZb3Pqql11R3Vr9gFYGsABS4RmgL0u++udunJX3XHdmn0AFgRwwFyhGeC6y75cBA8At111W3Vr9gEYAXz2MfjXwGChEWAQ8DWL5k9ggk5NT4DdPhzQJDQDzMmheUG3ZrYncTFHZ5qFZoAtOTTn6dbMtR2uAe4o9mS5VRUJjQDnsgy+W6dWIIBTik7VaNaRe76KRp1agZBHUEWnzmjUGJtl8G/lvUCXVr6vwyOPjskVu1STRn0WB7To0AgFsF3RuXWa7O/Pcu+o0KERCmA08MOjg7c02b9W0K3PD0C7opOVIe0WKa7eRk6beQPUKTrZGtLuNIXdLmETpJ/UM4+OvpGHp5DBDy9WCtsAmhWdXRbC5kEPey913zW0IC8jimjRpRA2b3rY2yFsBehQ3NTGaLoByt9lwlZIB0y92JaHrRkedsxGfcIi301n4cvkYR621ureViMBaFXMglRAO8cy2neIOABMVVyTjwS0053Rvk7EBaDTwwHyRDfcZ/uhGcfrx7pjDEYBGhWvwSqf7d3h7t4ocGwgnbv74OGAqz7bb3a1kXZGirgBHHKm8V3ghBPXW+izbRPwxFlL9ok4ApSEPbLKJ+933UiwGaDCiR+ukBkc4DBw3imnnd8tMsHp5ACnyJ1AxBnSHy/IRMo98kO+/13ALmB2LLZB0uGxnT7T6EGRKbc2YKKwDWC8zM4C3zDPbycEN9mGgRc7T/wL0SM/jtgDDClkAKSTwnMDGBf14CcpYoCF4kVkOQLSn8jZNHj3hak8Cgdcxl7Omh58LfZTY9IBV7CfdlODL1XkAG1DnkVKTDigmvhQZcIBs1wXGNvLTO0OSOjvACkLprbfkooy7WUjDYkDdEMyA+j3r0CqXy+CCQkJCeI/5C+Xr1Nj9eUFLAAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Stationery & Office Supplies', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAACvklEQVR4nO2bPWtUQRiFn1dFiwiCKPiJX72g1a6ton/AGK0sBEvBRrEVLVXQSv+AmgSLWAqmTCqRpDCCQSRisAhiuoh6LHYDIe7d+biT3N2985R33znzzmHncHf2XshkMpkaY6mEJF0EXpvZSgKtvcAV4BQwFDD0O/AWmDCzP2X78EbSBUm/JT1LoDUiaVnleC/peIq1+TR8QtLSmsmvl9BaNTIF85J2pVxrp4aHJM2sm3hF0pkILZM0l2jxq9zdiHWvbfhlwcTfJB0I1DuZePGS9ME175Z4C7gFXCr4bD8wLml7gN6xEr0U4cyBKAMknQPuO8qaQEgohpiVTDPYAElHgefAVo/yqyoRipvBtpBiSUPABLAnYNgTSbNmNhXUWXceAtMJ9dy0Q+9FZBgtSjro0B8O0BtOta6QLXAbGImcZx+tUNwROX7D8DKgHXr3Ss7VAJ6W1EiO04DA0HPRc6HoE4JFofcRmOkybjdwtsP1x5KmzazbWBcNSV6FZjZWYh4oCKElSUcc40zSK98QCwxBb1zri70TvGNmX7oVmJmAa5H6m0asAT98iszMq65KyvwWGAiyAVU3kJAx64Br0CAZEEU2oOoGqqb2BgSdB/Q4TUmj6y+aWdGxHTBYBhwCgs8Jar8FsgFVN1A1/ZoBU8DXFEL9asCj0gcdbWq/BbIBVTdQNbU3oF9DcNTjvBMA15lA7b8B2YCqG6ia2hvQryHYiWlazw0EMUgGLMTcHtd+C2QDqm6gagYpAw53+tvdlQuDZEAD+O9UGMcT8XkLRI672enrVpJfifW8NGMNaEaO68bnDdCcdxX00haYBeYSazpvjHrGgPYzRTeAVK+6fAIeuIp6xgAAM3tD612hnyWl3gHnzWzZVeiTAUmOn9exUPSBmY1JmgQuA6eBnQG6i8AkrZem/pZrMZPJZGrAP+s7Gd91ZI+yAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Tools & Home Improvement', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAB+ElEQVR4nO2aP08UQRiHf3MhJEJBYUJoNCEWVGJiZ+E1fANjKGhICB0XvgBfwEhjcr1/So8PAC1+A6OWUgM9mJDAY6HFBWfIMns377r7PuXsTPY3z83u7ey7kuM4juM4zhhAD9gHzmk+58BboDdJAdvGk8phK2euKWsva/izop8zKCXgQY0gVszlDJrcdfOfMmMdYBoAA0lLkj6HEL7d1betK6AvaU/SV2AIJH/oVq6AMYKkgSQk7cY6tHUF3GYHeBo70BUBPUmvUwe6wpNYY5cEzMYauyQgSkrAr6IpJsNlzqCUgC81glhxnDMo9RzwQdKKpE1Ji7mJCnEm6aOkT9ZBGgMwimyXR7G+fhO0DmCNC7AOYI3JbhB4KGlD0nNJ81M4xYtI21WsY3EBwCtJ7yUtFD71z1hjKJkAWJN0pPLibyQ9CyF8v32g2D0ACJKGsrnshrHJFwVYLVQfGOcaeEdDXoktJ9oPpnCuK0kn+vNS9MddHUsKiO7HQwjrBTP8Q+efA1yAdQBrOi9g6gCPgSPgMvFXNfn6fpP4O/kqZNX361LCetVvDbLq+3UpIaBq3T6rvl+Xdl5398AFWAewxgVYB7DGBVgHsMYFWAewpoSAqnX7rPp+XUoIqPqtQVZ9v/EAj4BD4CKxCzwF3rR2O+w4juM4TmP5DXXEtAtM4reOAAAAAElFTkSuQmCC') RETURNING id;"); // Hardware - Heating, cooling, flooring, paint, etc.
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Motor Vehicles, Automotive Parts & Accessories', 'Auto, Tires & Industrial', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAB+ElEQVRIid2VT0tVURTF121gBBEWSIOKwCihQTWxRArKgQPBSU0C7Ss40HmQfoOaBw3CpCAHQdC0aUWpCKEWCuVLRV+ivlLw1+Dug7vTeafX0BY8zrr77r3WO//2lf5bAG3AGPAGGADGgUlgEHgGrABLwCPg9L+KXwWqwCbwFPgOTAM9JhqjAlxsVLwFWEyIzAOfEvGAWeBIIwYPrOCdFQE8tFkErAHtwGXjASN/Ez8K1IAtoNeWaAs4EQn1uZp+F68Ch3IGIfmnK9oGRt3zh6imiJbupn9/IPLosrEm6YvxiqTbLmfMFxRFgaRRF+rOGYST0CtpwXifpCmX81p/4pXjl3IGp2xcknTG+GdJh13Ox4SBj9W/E8APW8c5t6Yz0Z40JeoOuve13AwCdiVtG2+yX0BR9x8mEBus23hD0jfj3ZJWXE5zQueY49WcQdjYVpUnSSr3YsbltCUMfGw+ZzBh42NJ54y/0O8n41rCwB/N94n3JdxF2wGeG1+k7EMBE1FN9qLFBs3WGjaAk5StYpO97hrQ72ruuHi+VVjBfUsecjzMKmANuAJ0RMbDWXEzaAG+AquUH5iA1HfAo7F2bSad7HXPYWAdeAvcApYT4hXgQkqr7qUBzkq6J+m8pHFJ7ZKOS3oiqVPSdUk7kl5KulsUxUIdqX2OXwDXyZR82SRGAAAAAElFTkSuQmCC') RETURNING id;");//category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Motor Vehicles, Automotive Parts & Accessories', 'Auto, Tires & Industrial', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA1UlEQVRIie2UvQ4BURCF7yxRIqIiosJjewLvoFlBI5EoEI+gFrF8milurp87N0gknGoz+51zZopd5/76agENYAjsea4TkAO9lPAmsIwEhxqnbL5Q0wroRviqsgdLeB2Yq2ENtAyeTPkiBtaAmcIbS7i5QM+cKrgF2pZw9YqlYKTQDuhYwz0/wDmciwccnXOV1OBAFxEp+YPMe341/K6yOPKayimwiEg4A/CR8P13XRBsa9LHL/ALnn/mNt1k+AWzNxQ8zgAGwAQoEn/RqCcH+m9Y8td0BU99R2bmnxZMAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Beauty & Personal Care', 'Cosmetics;Health, Beauty & Personal Care;Hygiene', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAADIUlEQVR4nO2bvY8VVRiHn1fQmPCxjTZqQmRjgYbKGAgojQUhgQTExZZ/YBsTC8TEjlgaNDYYxA5CBQUkfCTEhEDAimBloIFCAp2wiWzYx+KyZF3n7k7OzDkzYefp5pyc9/ee352Z8zHnwsDAwMAKJtoKpG4FvmwY5gFwGTgbEXPNsyqIOmV7/K5uKJH3KyVEEvgQuKiuzy3UVwMA3qP5I7UsfTYA4EBugdUtxroHnK4on2oQc7JB21q0NgqMQ7VJ+4jImmPfH4HsDAZ0nUDXDAZ0nUDXrHgD2pwHvEBdA2wE3mkh1i7gPnAnImaaxsuKukc9q860uDCaZ0Y9o+7uup//Q51Uf8vQ6XFcUd/tut8AqDvURwU7P89D9eOm+TeaZqqbgGvARNNEEvkb2BYRt1MDJBugvg7cpsCCZRn+BDZHxD9FVdWvxtyaz9QT6jZHo0FTnTXqdvVXdW6MZvZ9g8VJrVIfVCQyq+7PqPv5c43F/KWWm9Oon4z5Jb4poP3tGO3tubUXJnG4IoEnbdzyNbTXWj3POJQSL/W2eaui7GZEPEmMV5uIeAzcrKh6OyVeqgFvVJQ9SoyVwsOKsjdTAqUaUDV8lvyQUaWVNKQPq8EWY01psw3QLljxd8BgQNcJdM2KNyDLllgCAqeAi8+vPwW+AFblFu6DAbPA3og4t6DsuHocOA+8mlO8D4/AkUWdByAiLgPf5RbvgwE/L1F3LLd4HwyomtfXqWuFPhjw/hJ1H+QW74MBS63jv84t3gcDptRf1Bc7y+qEegL4LLd4qWHwJHCJ0eeyaWDdovqDwD71OqNl7VYg+wkxKGPAkYg4PH+hngGuVmhPADsL5PMfSjwCPy68iIgbwI0CurXo6h2Q/XBWXUoYML3wQt0CfFRAtxYl3gGH1EngAqPPaNOFdGtRKpEDFDj1mUIf5gGdkmrAbKtZtMPTlEapBtxNbJeTOymNUg04TdkPIcsxR/VB7WVJMiAibgE/pbTNxA9NTokkoa5Wjzo6ENEVz9Tv1eTRrPGMTN3M6D8BG4HXmsaryVNG76FTEfFHIc2BgYGBl49/AYo6o3PMW70cAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Pharmacy, Health & Wellness', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAE90lEQVR4nO2aXYhVVRTH/0snp4/xo6YpMqd0xmgsgqgUayJTqomwHKiHopeilyB6CXqOHgQpepWJCAmlUoicocgg6QNCsvwgkzQdxTRLS2tqnBmbkV8P+9rcOXPP2efuc+5N8/xhGM45a6//f61z9t17r72lAgUKFChw4cLqQQJcIWmBpHmSrpLUImmapKaSyaCkvyX9Kum4pIOSvjezk7XWVpMEADMlLZf0oKR7JF0X6OqQpC8kbZL0gZn9mY/CGgG4GVgDDJM/hoE3gZv+6zgnAZgO9ABjNQg8ijFgNTA9D+2ZuwDQJuljSfNTmA9L2i9pSNJfcn1fcr8FTZIuk9RW+u/DPkldZnawWs25AffJ/5TwtvYArwEPAdcDqRIOtAIPAKuAHQn+jwALah1nnMjpQH+MsF7grhy5bgXWAWcqcO0DmvxecgbwRgUxx4CHa8i5BDhUgbenVpxxQtqY/IN3FLixDtytwIEI9ygwt9bc5SJejQg4A9xbR/5FFV7AqnrxC9gdIX+vbuTjGt6JaNgV4idoGAQuj9waNrOREF+hABolXVp+z8x+r6eGAgX+B0g7M2uQ9KSkxZKaa6ooO05I2iLpbTMb8xl7EwC0SNos6Zbs2uqKXZKWmdlvSUZTUjhap/MveMlpXuszSvwCgHa5VVddKkc1AJJuMLP+OIMGj4PFqk3w/ZKOlP5M0pzSX1vOPCYXQ3ACOnIUs19Sj6SNcW8EmC+pW9Kzktpz4g1fnwAbKqy8qsUJ4Dngoip4pwHPAydz4F+fJQHfZiTfS4YVItAOfJdRw85Q8ilkK25+BczwcMwCZnlsZgBbM+gYAtKMdpOI52YgPQxc4/H/Im4ZPQa84LGdTXLpzYfqy/JAZwbCJR7fTbgixlmMAomFUGBpBj13xvlN+jSu9uQoDr1m9rnHpkUTR6AGuR2jWJjZp5I+DNQU6zspAS2BZC8HtkuDlwLbBSXgygCifjPbEdAuFcxsm6QDAU1jY0lKwMUBRL0BbapFX0CbS+IeJM0EQxKwO3oDeErSCo3vBMcJWgsMl10Pys0a3/JxpEBsLHkn4OfyC6Bb0pqUbTsr3OsGjpnZprJ7RwN0xcaS1AV864RK+CNyvTDARxS3ezjSIHYanpSAkCpvdOR4X25DNBSjkj7ycKRBrIaktxwifHb5hZl9A3TKrfAayx7NlFvxlaNH0kDZ9WlJfWa2PYkjJYISMBRAtFAukH9RGhYnDI3APE1OwCspt7oXBeiKjSWpCxwPIFoBTA1olwol38sDmv4S9yApAT8GEDVLuj+gXVp0KWyCdjjuQd4JkKSV+JefpLw3/tD5XBmoqfpYgEbgdODq62mP76lMrPYMAInzDuCZQC0jwLSqE1Ai3RZIegqIjt9R3/cBPwA7gcR+DdyBK2yEYGtQ8CXi1wNJwRVFMh+YADpwZ4FCsTrJv6+vbs6gfY6kr8lwZAboktvmujaDjk+CW+IqNyMZsg+u7LUBd5wuLW8r7uvLeu5whKznCYG+jCLOYhh4F3gcd3Y4ytMMPAGsJ7+Tpht98aXZHF2mbF0hDqfkdoYk113SHI6sFkvN7LMkg7Tb41vktpjOJ3xpZnf7jNImoEPSdiVUVs4xDEm6zcz2+gxTbRiY2R5Jj0k6t4+rOwxIejRN8FKVO79Aq6RHNLG8dS5hUK4sf8RrWaBAgQIFChS44PEPMB69xeBgPgAAAAAASUVORK5CYII=') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Tools & Home Improvement', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAABXUlEQVR4nO3ZS0oDQRSF4WtwB451Az424QIEwQX4GOoiIg5EJ0EXYISMegO6EjfgRB05iRPRX4q0EIhCNanursf5oCcZ1T251VT1NRMRERGZAwyAa+CN+Lk1Xrk1WyjACek5ChnAPekZhwygIj2VAgiFxDsAOAUugO1SA6jq376BW2C11AB+3ZQewFej7UB+ATjD0gOYlB5ApQBKOQoToAOOSc9hyAAG9RXzlfi9AJfz1+GlA0idAkAdUGkLLNI7wGIBrAFnwF29X0M/z0sdhdsE7APvdG8YQ/G7wGcPxbvr8Fbfxa8AT/Rj1GvxDrDT0z8/avxJrA3A3j+LbOMlOAHOgU2LBXDwV/VWChQA6gC0BRZZ7oAN4BH4oKv5fkyYFd/tfD8mwLTz+X5M8BfPdTUkBeBPHWA5Qh3gTR1gOUId4E0dYDlCR2Eems73swKsMwth6jvfFxEREbEO/ADiFbekRtGUMAAAAABJRU5ErkJggg==') RETURNING id;"); // Hardware - Heating, cooling, flooring, paint, etc.
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Motor Vehicles, Automotive Parts & Accessories', 'Auto, Tires & Industrial', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFjElEQVR4nO2ba4hVVRSAj1ia2mvGLMseakX0I7LQ+tGP/gVSSY9JA6UyzXLCAqnwkRmlZkUQURYJzWgjRlYaCSVB6WQhlNjLmXLKXhBlTS9LM535YjHr3k7Hvc/ed99z7r0D94P7a52z7tr77r32WmuvG0V16tSpUyc7gOHAZOBRYBXwItAGzALGAsOAa4AHgBaVrwGagTOBo4GrgOX6fivwEDBJZFGtQp/xzwN/8x8HgCeB04BTgKeBfTH5IeA54AyduMeBP7Dzuz4zPKoVgAHA3cD+hLGfARfoM7cAfyXk3wIXq/xqoBt/fgaaqj32CBgMvGQw8HVd6ufp8k7yDjBCdSwmjF7ZRtUc/CDgNYNhHwELgQ8thn8AHKs6llI+y6s1Ac8EGNsDnKvvi1PMitsqPfgbyjB2O3CpwWeUgzjbCZUa/MnAL2UaLPs3az4Vn1SJCVgdYNw/Ae98DszTzy7PdxbkPfhxuo9N/Am8a5FdCWwtYfDr4kGPBkem0ybJb8AJeU7AOsOXfgPcDBwDrDTIt+m7DcBuj0FslhPGcups8Xh/WV6DH62RW5LZKh9hcWxTVC5xwa8eAY71FwRO9AiYRD4sjwl40PBle+VM1zB3vUEuzvIofX8mGRxnmjO4mJlHuLvb8EWS5CzS/W+iLabD5QM6gIEetgzUZ9Noz3oCJgR694Pyq+oKsTnP/20lT3uaPY7ZU7OcgEWUxw4P7+2d6qrDlcwwjVuznIB28qUlwCapFaSxIavBH5nI4U1haLnR3uQAu6Y4dP4kviuLCRhv+YIejQqvsMibNO53IUdrY4BdDepj0jg7iwmYbvnVC1ndVMughupe3egw8v0ybJPUOtOVdRhaj0uyKyZfYpDvjMnnZL3/Y7qlXpjGfaG6i1hi8E0OI4oOSGt4adwVBQLc49C9JlR3EUuC82xM/oZB/kRMbqoaxZkYBQJc7tC9OVR3EaDLoPhhlQ2xyBfG3t/mMHJcFIgUXR26O0N1F7EkHwuASzRnN7Ffg6dBKc8UGBuVV4pPY0+o7iKGcnah+uIKbdGYXRKmNILzd81A09gbqruIJQXOksNy/xLL8mkcDNVdpD4BGLeAa4l/VcIKqPkt0O1wcl8Y5PP0MvN7jwmoeSfY5TjmttvqchLjA1/392NwqyPQedMgXxGTb6piIPR2XpXgDTG5XG8n2VhCvJ5nKNyWVzK0MyY33e5+4pBnlQytqkQyND0l3W2wVIukSeJ+YCRwXX9Phy+0KJ+qTRBpHNCLjjSkqNEQYFejR5B2Vp4lsV6yI6Qkdr1D555MSmIVKoq2Rtlf0q4vVacV4F7Hl31Z5gTkURaf5auvnMJor0aEp1u2hJwAr3pmjs0l2HO7Q5fYMspXn+/VmOlXXuuoHK2MhawfO4zuLOFqTJ7NtxKURBsbTZefQ1R+p0Euy3RoCW01szO6HJ0RZQ19zYymOvyNKh9puR7v0PtBudr+wWF4d6F9zmLDSR7tOflcjwva1prkvZh8RYph+/RocrHF0iAx2PM0WhLlBXC+xaFdFlslIf1ASV4WT5/w+q94nib5ttFiPn+lRnhExjGDpOHzgTssKbmJ+bkOPrbXTUWSOY4tkDcdhW6U3AGmBYbGO1Juk8tBkq/xFRl8AeApD6OSSL/fOVID8EikSiG7qK/EJEmiPBOd2lUmPURJ3lKPPsAjxPZhaVQt6CuKvmA5yho1ajMVLdoL3hqYG5hZyjuLqzb4AvpLzjWkzLLcL9JnZhiSl67YHyomAt+VMHjp/rg2qiXoW/ItiWjwkPqK0ZowtSR8Q4/WFEcBx8mlK/Cj45x/LKSjpGLQt/SbdDCtGj2u1UbJMcDx2t/ziMYUhT9N3SStbbqtJumfplarjmXab1y7f5qqU6dOnagf8i84W5AagP7l2QAAAABJRU5ErkJggg==') RETURNING id;");//category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Motor Vehicles, Automotive Parts & Accessories', 'Auto, Tires & Industrial', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA1UlEQVRIie2UvQ4BURCF7yxRIqIiosJjewLvoFlBI5EoEI+gFrF8milurp87N0gknGoz+51zZopd5/76agENYAjsea4TkAO9lPAmsIwEhxqnbL5Q0wroRviqsgdLeB2Yq2ENtAyeTPkiBtaAmcIbS7i5QM+cKrgF2pZw9YqlYKTQDuhYwz0/wDmciwccnXOV1OBAFxEp+YPMe341/K6yOPKayimwiEg4A/CR8P13XRBsa9LHL/ALnn/mNt1k+AWzNxQ8zgAGwAQoEn/RqCcH+m9Y8td0BU99R2bmnxZMAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Beauty & Personal Care', 'Cosmetics;Health, Beauty & Personal Care;Hygiene', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAACPUlEQVR4nO2bP2sUQRiHXzQQ0ZDUgiKSKkQFEVSMprFKUBCiXyB+A8FCFIxKUgf/1CpcCDlioZV2iaCVtYX4AQxaREVBk/jIkCHIMXuJzO7M7M77wFbHzu+d52bn7t7bFVEURVH8AU4Dbc/jAXAJ2CV1A7hCebwDDknGAgwfgH7JWIBhSjIX8F4asAn68EvqDp5I3UEF+CF1BxXgh9QdVIAfUkeAfcBRYAx/xuxYeyV1gIvAC+An5WPGfA5ckNQABoHXhGMJOCwpAIwCXwjPZ+Bs7MkPAavE4xtwJNbk9wAfiY/pG/TGEHC9oKAN4AlwxnwalPSJMgI8Bf4UZF4rZ1Y7BNgNrDgKWQMmpCKAyzajk09B+4jAuYJ34laA7NsF2SNVZ28B3HQU8KOMJb8dQF/B94wbVWdvATxyFLAkgQCWHfkPJWABC44CFgPmLzryFyRgAW1HAe1c8iV2AbHzpYRubxWoANEVEAj0EkD3AMrF/MiZBybtMQes57IJ/gbGHRnn7WuNFzDVJeduDgIOdMk5mIOA3m26To0XcLxLzokcBLS75DzLQYDhMTDwz/gDtq9IUwTMA1eBadvKdmHa6y+BV8DX/xw/aQHTHeeeLGhs+pKsgP2O89/kLuBtTgJmOs49ldslgG2imk1wBvhONSQtIAQqQAKugDnSoxVSwD3S405IAcfs3+CpsBH8Rgk2H29JhdmgkzcAPcD9yCvBZM+aWiQWbN7DZ1pXrRIemtrp0bKZw9EmriiKIg3gL31Q7N9UOef0AAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Drugs & Medications', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC6ElEQVR4nO3by4sUVxTH8Ts+QAUFYYIzEtC9joriRrJOVq4kW0MIGh0JulCI+FgmvhbR1WShZi36B6gkoKIEdOljkWXMBDOObrJQfHzkMtWh7ame7sxUVVd31Xdf99zfPefcx7m3QqipqampKR8YwS4cwmlcxHkcxVfYgKEwSGAF9uN33fEnzmB96GewGPvwj/nxBhMYDv0GPsUt2fAMn4d+wUwexzDOkrcYD2UHWzHdhaCnuINruIFHeNfFd+P9LP5fnMLmNt+vwV780SESvgh9KP5KFNhlW0txEK/nmBOG+0X8exyfz9qOzzDVpt2JUAawBc/nEP/dAtvfgVdtlsh12SnJx/MHMrIT0yGNH7Jov5Seb7G1BE9S7DzMykYpPd9ic08be+sHXnzTISptn7A7DLr4BnicYvf7MGg53w7cTLH9Uxh0zzdIts2tXAqD7vkGuJvShx9DD7e3R3IzPrsvQ5hM6cPBXp7q7mFVLh1Ij8Q0dvbySFvYICT1w1biFnllkTnfjgdYnWlHPu7TaHKUbuV6Lz1fSCQkuX9VOnvLIj63QcAJ6fyNZb0M+9zTIakOxaU2jW/K5PnMIyFuruYQ/ysWZSF+LAfxCx6EDuJjtXk0q7r9XzmJn/cgdBD/AhuzurG5nbP4/z0IXYjftmDxkVhbVywdJ8YOE95LbA8ZXlROKZ62kVCY55uM9YpZg1Co+Aju6y3/pUNhYd8Aa5WDGAlHCvV8BF8qP/mIj+CwcpN92DeDs6oqPoJflDfst4a8wQVV9HwDHFNV8RF8rTxMFxL2zWCTKnq+QSwiFHAELp/nm8G5yoqPxPv05HlJNcI+DfxcOc83g0+Sp2bVE98gPjbs8mXm4IlvgG8rkfNzkVRkso6EyXbPYUuJmXTIak74LRZeQr+B4WR1mO8S+SwpcS38xqYE+4RzyXP2TrxPfoOJv8MsD4OEmWvpscSrsZByOXnxHaPkZFJeG+l1P2tqampqwmw+AO2Rw/WiIGDFAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Sports & Outdoors', 'Sporting Equipment;Outdoors & Camping;Hiking;Hunting;Fishing;Biking', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAEPElEQVR4nO2a34sVZRjHv0+aGbEJ/boo291ItwQTFOumGyP6edWvTQsCY8OLCrzrOnRRwrv+AV2NfghhGwTSTd0VsSW1IpFFF5ViJbWK7rrqfrqYM3Cc887sO3Pmnfdo53N1ZuZ9n+/zPPP+mHnmSH369OnzP8ZiO+ACuF3SS5LWt059J+kDM/s7nlcNAWwGztDJDDAa27+gAE8AlxzBp1wCHovtZxAAA34sCD7lGNCTU7crgHUewaesrUv3uroM1cBwibb31CXaSwn4t0Tbf4J5EQvgxpzVP8sMsLwu3Z4ZAWY2K2mPR9N3zGwutD9RAJYA7xfc/feAJbH9DArJdvgy8CVwtjUtvgC2XDPbH7AM2AH80bqzl1v7+5bYvgUHWAMcKRjmb8f2MRjAg8BfHiv99ti+1g7wCH7bXDolnovtc20AzwMXPINPOQc8FNv3rgFeAOZLBp9yErgrdgyVAUaBixWDT/kKuCF2LKWpKfiUidjxlAJ4scbgU96MHZcXwDMUV3WqMg9sqtvfWh8tgfslTUm6qaKJI5KWS1qTc31G0m5J+yTNF9g5Z2ZF18MAfNLFHf4cGABWAr92OVqaL54Ct1B93k8A17fZuhc40UQC6qwHrJK0tEK/dyVtNbOL6Qkz+0XS45JO1+RbLnUm4GzJ9pclvW5m282M7EUzOyrpaUln6nAuOCTFjDJzd8zT7kZgKtQUqHsXeFbSx552j0p61Mz+9LQ9JGlI0jKP5tNmdsrHbu0Ar5C8yPgwDdwRxdGQAIPARyWScGtsn4MAbAK+90jCZ7F9DQawFHgDOL1IEh6O4V/pRRBYIelJSWslrWyd/l3StKTDZubctoDbJI1Lek2Sq7S9x8zeCqXfNcAwcIDiys4FYD/Jip1nZz1w3NH3wyb0qwa/DZhbZAi3M0vBPg8cdPQ52JR+2eB3lRDOMt5tAkLolwl+W47hBeAbkpeYidbvhZy2HXfCNwGh9H2DH8Y97A4BqxztVwOTjvazwGDZBITU903AAYexcQq+z5F813MN2X0VEhBM3yf4FXSutoeKxDNOZO/EHDDgm4DQ+j4J2JwxsIBj2BX0H6FzTo62XV8sAUH128mrBzyQOZ4ys599HTCzn5T8ubGddb79m9TPS8CdmeNjvuIFfbI2i2hMPy8B2QpNlbpBtk9H1aeAxvTzEnAyc5xXpi4i2+dEib6N6ecVMX/IHG8EVpvZcR9l4D5JGwpsfu3o1n4utP6iBm52bEOTJbahT7vZhmLrp4YmHFvVriInWuK7Hf32lhLvAX0BQySPkVkmgRFH+xFH5gHOA3dfbfqp0TGHQUgeMqZI3r33A9+S/zLyaiXxHtBPnRjPMezDzq7Ee0A/dWIM93DM4zywtRbxHtBPnRgkWZiKKjNzwF6qzrkI+lWKogOSntKVRcnflHzpOWxmZb8RXlX6ffr06XNN8R8f+FLdSRxelgAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Real Estate, Property & Housing', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABNUlEQVRIie3TsUqcURAF4G+DgqUgKZSAEJ8gwr5GumghFqlCCGKVVxBC0gbSaroQMBb6AipbaJpI0CIWqSVVBAsJx8K77Lr8/+8uSCCYAxdmhplz7tw7w3/cglbXSJI7JW61WvDgLkmrUCWwh6d4ifMS28VqX84rnBb7NxbKWaxVSg9PkrxPspRko8SeJxlL8rP4s0kOin1W6g+SHHZJmjpo4yNeYBl/sIMlbDW8xmd8HwxWCXzACp5hG/sYxyQ2GwTmMTeMwAzGSsEPfMEjXKCDXyXvBF9xWfzHmBgkqxrTDt5gCu+w5voD23hdOlvHWcl/W+JdfKI3pv/+HoyEwREcBn9/k5M8TPKtb/Fqb5ybOE4y3ahWRd4vUOcPJVJHPkIHzSJJjmoKRhVIkqNuzj3bg/uJK/g6RdFvw8VbAAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Real Estate, Property & Housing', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC4ElEQVR4nO2ZPWgUURDHN1FEQQ0RIxiRoE0g2IiNgfRaGBRjSLAINn4UVkGwUJtUIY2VgYhgJ3jgV6EgEQIWNqIighFUEFQQsdDgF8b4k4ezOLfs3u3d7t7t5uYHw3HzZt6+98+8j9t4nmEYhmEYhgIosfwoeXHBBMAqgOWHLQHP9oB0NsF5YBToktiNwBjwpkLOe2CX2JGImN8qZrBKOe9TsWE2l9USeAasj8jpAl5H5E2ruJXAp5CYRRWzrYoAPVXGfyMrAQ6omL3ARfepfCMReXsC/V9OSwDgKHAsYE+yEqBb2lcD38X3DVgr/nUhOZ+BVYH+96cogFs6cUhFgC3SvgL4qPxXgRmx4ICuqH4H5HMN8LWIAhxSMTuB+zEePCLxG9zaVPnXiyjAS7frB2J3A7cj4n/6myZwEPjiNkH5PpaGACFjP5elAMhxNxiS4yb0i3LuqPYL4utXFbFYRAF8HgNDgbwpyjmu2l6I76zy3UthCdwFZpW9avRNcEblbVX+JWCzatsutkn5ThZpD5hV7e6s1ehJ+cvgQYznOMH+FEWAd0C7GuCC+D+oja1DxZ9W/Q2578r6VNvDogjgGFUx3e5m6N8NxHeY//Qq/1vKmVJtZ0IEaAc6A9YTIUBniN0kIwEWgtdalbNDqsHxXPl7Q/p5pNr7ggJE9N9Rw2+BUtab4BxwSu7+J4Brcub7PAUmxW6F5C/JieHH/BDfZAU7r/Knq8TOZy1AETEBPKuAmGBLgJbfA8alCrKy4L0A8WX5zHEvL5D0nC46mABYBWBLoMB7ANCmXnFVJGkFxHkGcMn/qZ6ryTsaJEBjRKDGyTdYgGxFoI7JN0GAbESgzsk3SYB0ReDf5N2/vOoios9+YDhg/SkKgIy5rWl/eZ9EA0gmQLJKIIXJ50CA+kQgpcnnRIDaRQAmUnpwXgRwTDTrhchwQksLeyfoNakC8oIJ4FkFxIRWXwKGYRiGYXitwF+FAy8M28uvbgAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Luggage & Travel', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAC9klEQVR4nO2bPWjVUBiG38+K2C7iLzhZVyc7O/iDojhY3HSxautYFQQXF0HB2cEiKCLqIEoFCwoiaKU4V0REKFpxrFY61EFrfRyuQr3kJLnJPTkxNw90yTnN95435+R+J/ki1dTU1HQw5uOkwFFJ+5sOz0gaNjN8xCwVwAGiORFaWyEA3cB8hAFzwMbQ+goBGHXMgjuhtRUCcMRhAEDz/aF6AGuABYcBU0B3aI3eAZ7FzIKLofV5BzgVY8B3YEtojV4BNgG/YkyYALzkIqUBmIwxAGAotEavAOcTDJgFNoTW6Q1ga4IBALdD6/QK8CGFCXtCaFtWUJyxFH1GqGpuAOxMMQMALoTW6gWgC/icwoDCc4PIJQAMAveW/A3mCWJmi5Iep+i6QtJVQucGwLGmK7MAbMt5zoMplwF5Dc8NsDlC1CdgXY5z9gDfUhoQPjcAPkYIe0SO6Qk8bGEW3GrneLKIvekQdibHOY+3YADA7naOKYrlMW3jkgYijl8C5iV9zRBvZYv9R4BzGeL8kDRtZq+TOjqnM9AraTpD8DLxTtJJM3vq6hC7noFpSb3tVlUwi5IOm9n9qMakVHi8/XoKp0vSNRy/YEkGvGi/niCsknQoqqETZsBf+qIOFrUbLAORr+SSDNjhQUgoJqMOJhmw3YOQEMxJuhvV0AkzYFHSkJnNRjU6DfiTCP3vOcBbSXvNbNTVIS4Vdl39BUnDypYKr5d0pYX+U5KypsLvzexNhv9t4GkzNFi2zVCcWB/b4bEWBh9uO0wHPRBx3QSb1/9PNTYUX3LE2iepJ2Xfs2Y2kyNWalw3wWWSlu6enpjZy5yx+lP2m5B0I2esckGJH4sXArAr5dqv7IuRyykGX92yGUr8ctQ7QF+KwVf39Th1gQSvEgyobokMjSKpOKpdJAWcjhl8NX/zlwI8jzGg2oWSwFo6uVQWGIi5+h1RLP3AMfjql8vT6R9MAP2Oq1+6T2biHormYbX+fZ4gNT6auu4pXk1NTU1NFn4DHMdp490i7dkAAAAASUVORK5CYII=') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Business, Industrial & Scientific', '', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAADDUlEQVR4nO2ZvWsUQRyG30lSaCJiVLSKYAoxooJIIIWiWImFYJcEYmnhPyD4B6iNpcFCUAjEkAQsRC20ERTxA4JFijSCImgR/IhJUHPJY3GJKN7uzsztZO5knubgbnbe9/fu7MzsnJRIJBKJRCLRwABbgBd4ENt73dRTvE0AbY5mdknqltQpaVbSlDFm3q80K71OSQ8lHQ6lYWOiFTgLvK4R8BJwBzgUQLcTeOV7521HQJGJrcAjC50KcKGk2ksr3iaAzEcA2CDpnqQ+C8+tkq4AFWPMVftS/9JrkXRK0vHVz56MppOSxn00XA1d8gj8J7DfQ6sLeG7R/xjgNG95QXXoL3gEAHDbUWsTMN0wxa+aGvIsPhTjBCq+JeP7eMvOv0xIGjTGVEJ0nhXA9hBiHjxVwOKl7AAWQgk60iepP6RAVgAzIUUdaJV0CxgIJWBqfQn0SJrO+r2AKUmXHdp3SLomqT2nzbKkIWOM0wpTF8B9zxn7tIfWGYqX3QowGKLWLFPdwFfH4icBn1EjoAe4CbwFlhslhKPAF8viPwB5w9hF9xgw7xh+Tcowsxu4C6wUaK0A+0qof023lBDK8iNgDzBcoHe9NEGVE0KZfgS0A7M5eovAtpI1jwBzoQLI2gfUxBizKOlGTpONks659Gmh+UTV1+NvZfb7u3/XC4AuSW+UfZbwXlK3MWapHmM1dA9I2ut6nTFmokwfa2bGCkZesJ1bQwD0FgTwMrbH4ADPCkKwOUprXoD+ggDWb98eA6ANeJcTwBLVCbOhcVoG/2T1kGI4p0mbpPO+/TcFVM/v83Zqn4CO2D7z8B4BkmSM+SxpJKdJp6ShejQaHqrvCFmvrwAzVP/0+H8BHhSsCCdje6wb4CAwCnwsKHa9mQceAwN4HsbYFD8I/Ihaph0jlP24Ub3zzVD8Ghdd6iscMsCopGZ6uZmTtNMY892msc1wOVGfn3Vns6Re28Y2Aezw9xINa882AYSZWcNiPRH+3xsUC1IAsQ3EJgUQ20BsUgCxDcQmBRDbQCKRSCQSiUQsfgGI7rZpsWTP5QAAAABJRU5ErkJggg==') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Illegal', 'Banned and/or prohibited items', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAFDUlEQVR4nO2bzW9WRRTGn2ljEaVFSghl40fStC5cYcNOSXVjQmo0fqAsWGI1oWyQmFQihkDUFQQ2xqVGoqa6smGPfwENkmoMpUHBapR+CG2I/bmYt0rrmfs5975NeJ/lvTPnOee5c+ecOzNXaqGFFlq4h+HqIgK2S3pCUr+kRyQ9JGlT4/aCpD8lXZX0g6QJ59xMXb5VAqANeAY4C3xPflwCzgCDQFuz48kMYCvwHjBdIOgQrjZsdjc7viCAzcBHwHzEwNdiHvgQ6Gp2vKsA7AOuVxj4WvwCvB7D91KTYONJfCLp1RzdFiRNSbohaV5Sm6QHJfVI6pV0fw5b5yS94Zybz9EnDoDHgR8zPK1FYBw4DOwkYULDT5z9wAFgrNE3DZNAX52xC9gF/Jbi2AzwPj79FeXZAozgJ8E0roGYMSY5tYvkie4WcATIM5TTODuAg8DNBN65ykXAD/ukJ3+BCocj0IN/nZJGQjX8QBfJ7/wyMFQJ+Wo/HDDa4LMwCXRWQfxFQvArWAT2RCe3/dkP3An48Xlssn0Zgm+WCKGRsDcWyWbCRU6IvE4RRgM+/EyMVwFf3lq4AAwRztW1iICvHUIT48myxrdip7xbNGZbYM86EKEHO0XOUeYDCjgWCOztNe3WgwgjAf6jRQ22YX/SzgAbjfbPAbcDTiwBz5eOMtnf+4Apg3uKIusJwLOBYI4l9GnqSAAOBbh3FzF21jB0m5TavpkiAN0B7tNFjFnLWOMZ+zZThG8Mzom8RrZj5/jDOWw0RQRg2OBbBrblMRJ6/3fmdKZ2EfAfbBYG8xh5yzAwT4HZtG4RgHbsbDRstQ8F9Khxbco5t5zXIefct5JekrRk3N4gaSymCM65vyX9ZNyyYgoKYK26Xi/hVK0iSLI2VTZbDUMCbDKu/VXYHf0rwguSFo3bGyR9TbxiyVoktWIKCmCh9Daac+68pBdli9Ah6atIIpC1YUiABeNalFWWhggvy34dOiR9GeF1sJ62FVNQgDnjWvY8moIa5oQdxrVZq2FIgCnjWm+RNBhCVSIA7QpksTxGQoVQf16HMnBFrROAgYCtXIVQqBQ+kDvCbHzRRMDvSaxFvlK4YeiSYWgsd3TZ+aKIAJw3+l8s4tCZgCNbchvLzllKBPzSmNX/VBFnBgOOjBSOMBtvYRGA44F+TxdxxAFXDGPTQEepKNO5cy+vAQ9gb9tNAcWKOPyxFAsHS0eZzp1rJADvBNoWWxRtGO3GLy2vxU2gp3SU6fyZRMCfKbBGzCxl5yzgg4AD49RweiuDCEPAd4H7J2I40IU/k2NhNEKMWXxIEiG0PXeNWLvEwGsJ5PujkKT7kCSChVdiO3AuQHRnHYrwaRXknfjDB6GRMErRdJPPjyHCwx7gMmAufsQg78NvjYUwToXZAT/bhyY8gF+B3qr4V5wYwE6NK7iJ36iMViwBG/F5PlQcgU95T8biTHNoIGUkgK8YD1Fiexpf2x8n/UjejdqCv8u5PsJzwt1YxG9XDeM3LdoTbLY3xD2C/6rLMuFdpsSwL3tUtlPSx5LynNtdkl+3Xzkqi/x64w5Jj0n639Z7Aj6T9KZzzlzvqw3AXvyZnLpwjdh5vizwafIkyRNkWcwCJ6jiHGAs4D+gjmKf1iiKK8C7VLgYEx349YTdwGlgguTiZS2WgYvAKeApKiyw6vxpapv++2nqYUndWv3T1B+SpiVNyv809XtdvrXQQgst3LP4B1dCXq0t0BR7AAAAAElFTkSuQmCC') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Miscellaneous', 'Others;Non-classified', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAAy0lEQVRIie2UQQ6CMBBF3xjPACs9gbdgw0m8pSvFC8gJYOch/C5sk6ZpQ4EYNrwV06Z//h+gsDMHSa2kUcsZJLWhpkUNBuC00udoZudcA5UomJml1v35cP+wzGQ5e4PtGxyXHCr92mCrEZkDeAKPoPbUQO+ee6DO/RtTCT5Aahw34OLEGzN7S6omtH6zdXSS7sF6J+ke3TsvLyqpcrXi95NLEDuP69i5T1SWIHaeSJhyPitBzjkACec90KQE/n5dxwmuwLhG3GnslPMFJKbl/7w5wk8AAAAASUVORK5CYII=') RETURNING id;");
category_id = database->get_integer("INSERT INTO categories (name, description, thumbnail) VALUES ('Miscellaneous', 'Others;Non-classified', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAACbUlEQVR4nO2aT4oTQRSHS5mlbkTRCyg4oxdQZ+sJTFzPKUTcCK7nAEJWji4mzBHcZDMqguNC8ABKZlZDsjIb88ljSgjdVd1J6k+69H3QkG5eNfX7db1X1Z0yRlEURVGUTQBcMqUA3APeAWd0l1Pbx53Y4p8CM8pB+tqLJf5+YeL/MosyErgYUqVyEMOAM8rlNIYB80yd/SB5G3DsO+45j2FALoaB/RQTaqgBoZAPHQEm7EFpCqA1oI7WgFDIh9YAE/agtAagNaCO1oBQyIfWABP2oLQGUHgN+CFpEHAcl25AEtSAUCgcNSAU3Bw3FKOvwAR43xAjBa+tCI5aHu5ok0Ww54l9tfAV+RzY9cRJh73rAOAW8L1B/Ovq/4q51wE9R9wdxyf0L6sasI74rhjw2BE3WcWAdcV3xYBrdtgvcriCAaN1xdt79jdqgCA5L8PeFsFD4LpZ3gACxHtHjkllAPACGNvjeUP7G8ARMAVOrEnDHOJTGnDkuNb3tK/GntspMrn4lAZMHdcGnvau2EkO8SkNOHFcc6aBI3buaR9dfEoD9oBPC+cfgSue9rsLs4OIf9lSA6KJT10Et4CHwAP5bWOv2uI4kH1FlSlS1gm37fkwh/ikBph6nBjyecm08K0DQsTnnQZNPe6RI268ggHeb4LATeBbg/jBxhdCXKRDlZ+hBiwp/nIXlsJblcIoPAsxYJV3g5QGzB333fdsVNqzU9zUivRtaJINUW2bpPpL5Hy/ZZPU7/99m9w4hgFvKZc3MQzYKXSr7C9gO9gAwebarDDxT0xMgG2bDvL621WkbwfA3ajiFUVRFEUx/yJ/AKs6hY9h/vEkAAAAAElFTkSuQmCC') RETURNING id;");
//category_id = database->get_integer("INSERT INTO categories (name, description) VALUES ('Collectables & Art', '') RETURNING id;");
//category_id = database->get_integer("INSERT INTO categories (name, description) VALUES ('', '') RETURNING id;");
// NOTE: Categories also act as tags to be used for filtering specific products
@ -247,7 +248,7 @@ std::string neroshop::Backend::getDatabaseHash() {
//----------------------------------------------------------------
QVariantList neroshop::Backend::getCategoryList(bool sort_alphabetically) const {
// Do some database reading to fetch each category row (database reads do not require consensus)
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
sqlite3_stmt * stmt = nullptr;
// Prepare (compile) statement
@ -284,7 +285,7 @@ QVariantList neroshop::Backend::getCategoryList(bool sort_alphabetically) const
//----------------------------------------------------------------
int neroshop::Backend::getCategoryIdByName(const QString& category_name) const {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Execute sqlite3 statement
int category_id = database->get_integer_params("SELECT id FROM categories WHERE name = $1;", { category_name.toStdString() });
@ -292,7 +293,7 @@ int neroshop::Backend::getCategoryIdByName(const QString& category_name) const {
}
//----------------------------------------------------------------
int neroshop::Backend::getCategoryProductCount(int category_id) const {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Execute sqlite3 statement
int category_product_count = database->get_integer_params("SELECT COUNT(*) FROM products WHERE category_id = $1;", { std::to_string(category_id) });
@ -305,7 +306,7 @@ QVariantList neroshop::Backend::registerProduct(const QString& name, const QStri
const QString& product_code,
int category_id) const
{
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
QString product_uuid = QUuid::createUuid().toString();
@ -322,7 +323,7 @@ QVariantList neroshop::Backend::registerProduct(const QString& name, const QStri
}
//----------------------------------------------------------------
void neroshop::Backend::uploadProductImage(const QString& product_id, const QString& filename) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
database->execute("BEGIN;");
@ -390,7 +391,7 @@ void neroshop::Backend::uploadProductImage(const QString& product_id, const QStr
//----------------------------------------------------------------
QVariantList neroshop::Backend::getProductImages(const QString& product_id) {
// Do some database reading to fetch each category row (database reads do not require consensus)
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
std::string command = "SELECT id, name FROM images WHERE product_id = $1";////std::string command = "SELECT id, name, data FROM images WHERE product_id = $1";
sqlite3_stmt * stmt = nullptr;
@ -429,7 +430,7 @@ QVariantList neroshop::Backend::getProductImages(const QString& product_id) {
//----------------------------------------------------------------
//----------------------------------------------------------------
int neroshop::Backend::getProductStarCount(const QString& product_id) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Get total number of star ratings for a specific product
unsigned int ratings_count = database->get_integer_params("SELECT COUNT(*) FROM product_ratings WHERE product_id = $1", { product_id.toStdString() });
@ -437,7 +438,7 @@ int neroshop::Backend::getProductStarCount(const QString& product_id) {
}
//----------------------------------------------------------------
int neroshop::Backend::getProductStarCount(const QString& product_id, int star_number) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Get total number of N star ratings for a specific product
if(star_number > 5) star_number = 5;
@ -447,7 +448,7 @@ int neroshop::Backend::getProductStarCount(const QString& product_id, int star_n
}
//----------------------------------------------------------------
float neroshop::Backend::getProductAverageStars(const QString& product_id) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Get number of star ratings for a specific product
unsigned int total_star_ratings = database->get_integer_params("SELECT COUNT(*) FROM product_ratings WHERE product_id = $1", { product_id.toStdString() });
@ -471,7 +472,7 @@ float neroshop::Backend::getProductAverageStars(const QString& product_id) {
//----------------------------------------------------------------
//----------------------------------------------------------------
QString neroshop::Backend::getDisplayNameById(const QString& user_id) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
std::string display_name = database->get_text_params("SELECT name FROM users WHERE monero_address = $1", { user_id.toStdString() });
return QString::fromStdString(display_name);
@ -487,7 +488,7 @@ int neroshop::Backend::getCartMaximumQuantity() {
}
//----------------------------------------------------------------
int neroshop::Backend::getStockAvailable(const QString& product_id) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
int quantity = database->get_integer_params("SELECT quantity FROM listings WHERE product_id = $1 AND quantity > 0", { product_id.toStdString() });
return quantity;
@ -495,7 +496,7 @@ int neroshop::Backend::getStockAvailable(const QString& product_id) {
//----------------------------------------------------------------
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListings() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
std::string command = "SELECT DISTINCT * FROM listings JOIN products ON products.uuid = listings.product_id JOIN images ON images.product_id = listings.product_id GROUP BY images.product_id;";//WHERE stock_qty > 0;"; // image.product_id must be unique in this field to prevent duplicate listings!
@ -551,7 +552,7 @@ QVariantList neroshop::Backend::getListings() {
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListingsByCategory(int category_id) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//std::string command = "SELECT DISTINCT * FROM listings ORDER BY date ASC;";// LIMIT $1;";//WHERE stock_qty > 0;";
@ -614,7 +615,7 @@ QVariantList neroshop::Backend::getListingsByCategory(int category_id) {
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListingsByMostRecent() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//std::string command = "SELECT DISTINCT * FROM listings ORDER BY date ASC;";// LIMIT $1;";//WHERE stock_qty > 0;";
@ -671,7 +672,7 @@ QVariantList neroshop::Backend::getListingsByMostRecent() {
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListingsByMostRecentLimit(int limit) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//std::string command = "SELECT DISTINCT * FROM listings ORDER BY date ASC;";// LIMIT $1;";//WHERE stock_qty > 0;";
@ -734,7 +735,7 @@ QVariantList neroshop::Backend::getListingsByMostRecentLimit(int limit) {
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListingsByOldest() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//std::string command = "SELECT DISTINCT * FROM listings ORDER BY date ASC;";// LIMIT $1;";//WHERE stock_qty > 0;";
@ -791,7 +792,7 @@ QVariantList neroshop::Backend::getListingsByOldest() {
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListingsByAlphabeticalOrder() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//std::string command = "SELECT DISTINCT * FROM listings ORDER BY date ASC;";// LIMIT $1;";//WHERE stock_qty > 0;";
@ -848,7 +849,7 @@ QVariantList neroshop::Backend::getListingsByAlphabeticalOrder() {
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListingsByPriceLowest() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//std::string command = "SELECT DISTINCT * FROM listings ORDER BY date ASC;";// LIMIT $1;";//WHERE stock_qty > 0;";
@ -905,7 +906,7 @@ QVariantList neroshop::Backend::getListingsByPriceLowest() {
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getListingsByPriceHighest() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
//std::string command = "SELECT DISTINCT * FROM listings ORDER BY date ASC;";// LIMIT $1;";//WHERE stock_qty > 0;";
@ -961,10 +962,20 @@ QVariantList neroshop::Backend::getListingsByPriceHighest() {
return catalog_array;
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getNodeListDefault(const QString& coin) const {
QVariantList node_list;
std::string network_type = neroshop::Script::get_string(neroshop::lua_state, "neroshop.monero.daemon.network_type");
std::vector<std::string> node_table = neroshop::Script::get_table_string(neroshop::lua_state, "neroshop." + coin.toStdString() + ".nodes." + network_type); // Get monero nodes from settings.lua////std::cout << "lua_query: " << "neroshop." + coin.toStdString() + ".nodes." + network_type << std::endl;
for(auto strings : node_table) {
node_list << QString::fromStdString(strings);
}
return node_list;
}
//----------------------------------------------------------------
QVariantList neroshop::Backend::getMoneroNodeList() const {
QVariantList neroshop::Backend::getNodeList(const QString& coin) const {
const QUrl url(QStringLiteral("https://monero.fail/health.json"));
QVariantList node_list;
QString coin_lower = coin.toLower(); // make coin name lowercase
QNetworkAccessManager manager;
QEventLoop loop;
@ -974,18 +985,20 @@ QVariantList neroshop::Backend::getMoneroNodeList() const {
loop.exec();
QJsonParseError error;
const auto json_doc = QJsonDocument::fromJson(reply->readAll(), &error);
// Use fallback monero node list if we fail to get the nodes from the url
if (error.error != QJsonParseError::NoError) {
neroshop::print("Error reading json", 1);
return {};
neroshop::print("Error reading json from " + url.toString().toStdString() + "\nUsing default nodes as fallback", 2);
return getNodeListDefault(coin_lower);
}
// Get monero nodes from the JSON
QJsonObject root_obj = json_doc.object(); // {}
QJsonObject monero_obj = root_obj.value("monero").toObject(); // "monero": {} // "wownero": {}
QJsonObject clearnet_obj = monero_obj.value("clear").toObject(); // "clear": {} // "onion": {}, "web_compatible": {}
// Loop through monero nodes (clear)
QJsonObject coin_obj = root_obj.value(coin_lower).toObject(); // "monero": {} // "wownero": {}
QJsonObject clearnet_obj = coin_obj.value("clear").toObject(); // "clear": {} // "onion": {}, "web_compatible": {}
// Loop through monero nodes (clearnet)
foreach(const QString& key, clearnet_obj.keys()) {//for (const auto monero_nodes : clearnet_obj) {
QJsonObject monero_node_obj = clearnet_obj.value(key).toObject();//QJsonObject monero_node_obj = monero_nodes.toObject();
QVariantMap node_object; // Create an object for each row
if(key.contains("38081") || key.contains("38089")) { // Temporarily fetch only stagenet nodes (TODO: change to mainnet port upon release)
node_object.insert("address", key);
node_object.insert("available", monero_node_obj.value("available").toBool());//std::cout << "available: " << monero_node_obj.value("available").toBool() << "\n";
////node_object.insert("", );//////std::cout << ": " << monero_node_obj.value("checks").toArray() << "\n";
@ -994,6 +1007,7 @@ QVariantList neroshop::Backend::getMoneroNodeList() const {
node_object.insert("datetime_failed", monero_node_obj.value("datetime_failed").toString());//std::cout << "datetime_failed: " << monero_node_obj.value("datetime_failed").toString().toStdString() << "\n";
node_object.insert("last_height", monero_node_obj.value("last_height").toInt());//std::cout << "last_height: " << monero_node_obj.value("last_height").toInt() << "\n";
node_list.append(node_object); // Add node object to the node list
}
}
return node_list;
}
@ -1073,7 +1087,7 @@ QVariantList neroshop::Backend::validateDisplayName(const QString& display_name)
//----------------------------------------------------------------
// TODO: replace function return type with enum
QVariantList neroshop::Backend::checkDisplayName(const QString& display_name) const {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database->table_exists("users")) { return {true, ""}; }
std::string name = database->get_text_params("SELECT name FROM users WHERE name = $1 COLLATE NOCASE;", { display_name.toStdString() });
if(name.empty()) return { true, "Display name is available for use" };// Name is not taken which means that the user is good to go!
@ -1091,7 +1105,7 @@ QVariantList neroshop::Backend::checkDisplayName(const QString& display_name) co
//----------------------------------------------------------------
// TODO: replace function return type with enum
QVariantList neroshop::Backend::registerUser(WalletController* wallet_controller, const QString& display_name, UserController * user_controller) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Validate display name
auto name_validation_result = validateDisplayName(display_name);
@ -1129,13 +1143,27 @@ QVariantList neroshop::Backend::registerUser(WalletController* wallet_controller
}
//----------------------------------------------------------------
bool neroshop::Backend::loginWithWalletFile(WalletController* wallet_controller, const QString& path, const QString& password, UserController * user_controller) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::Client * client = neroshop::Client::get_main_client();
client->write("Mr server, I am logging in with my wallet file\n");
client->write("Mr server, I am logging in with my wallet file 2\n");
client->write("Mr server, I am logging in with my wallet file 3\n");
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Open wallet file
if(!wallet_controller->open(path, password)) {
throw std::runtime_error("Invalid password or wallet network type");
return false;
}
std::packaged_task<bool(void)> open_wallet_task([wallet_controller, path, password]() -> bool {
if(!wallet_controller->open(path, password)) {
////throw std::runtime_error("Invalid password or wallet network type");
return false;
}
return true;
});
std::future<bool> future_result = open_wallet_task.get_future();
// move the task (function) to a separate thread to prevent blocking of the main thread
std::thread worker(std::move(open_wallet_task));
worker.detach(); // join may block but detach won't
bool wallet_opened = future_result.get();
if(!wallet_opened) return false;
// Get the primary address
std::string primary_address = wallet_controller->getPrimaryAddress().toStdString();
// Check database to see if user key (hash of primary address) exists
@ -1162,7 +1190,7 @@ bool neroshop::Backend::loginWithWalletFile(WalletController* wallet_controller,
}
//----------------------------------------------------------------
bool neroshop::Backend::loginWithMnemonic(WalletController* wallet_controller, const QString& mnemonic, UserController * user_controller) {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Initialize monero wallet with existing wallet mnemonic
if(!wallet_controller->restoreFromMnemonic(mnemonic)) {
@ -1190,7 +1218,7 @@ bool neroshop::Backend::loginWithMnemonic(WalletController* wallet_controller, c
//----------------------------------------------------------------
bool neroshop::Backend::loginWithKeys(WalletController* wallet_controller, UserController * user_controller) {
/*
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
// Get the wallet from the wallet controller
neroshop::Wallet * wallet = wallet_controller->getWallet();
@ -1236,34 +1264,16 @@ bool neroshop::Backend::loginWithHW(WalletController* wallet_controller, UserCon
//----------------------------------------------------------------
//----------------------------------------------------------------
void neroshop::Backend::startServerDaemon() {
// on launching neroshop, start the neromon process, if it has not yet been started
int neromon = Process::get_process_by_name("neromon");
if(neromon != -1) {
neroshop::print("neromon is already running in the background", 4);
return;
}
/*server_process = new Process(); // don't forget to delete this!
server_process->create("./neromon", "");
// show all processes
////Process::show_processes();*/
#ifdef Q_OS_WIN
QString program = "neromon.exe";
#else
QString program = "./neromon";
#endif
////QStringList arguments;
////arguments << "--ip" << "12.0.0.1";
/*QProcess * process = new QProcess(this);
QString folder = "";
process->start(program, QStringList() << folder); */
/*int exit_code = QProcess::execute(program, {});
if(exit_code < 0) { throw std::runtime_error("program either cannot be started or has crashed"); }*/
// Note: If the calling process exits, the detached process will continue to run unaffected.
qint64 pid = -1;
bool success = QProcess::startDetached(program, {}, QString(), &pid);
//if(!success) { throw std::runtime_error("neromon could not be started") }
if(!success) { throw std::runtime_error("neroshop daemon process could not be started"); }
std::cout << "\033[35mneromon started (pid: " << pid << ")\033[0m\n";
}
//----------------------------------------------------------------
@ -1272,8 +1282,9 @@ void neroshop::Backend::waitForServerDaemon() {
}
//----------------------------------------------------------------
void neroshop::Backend::connectToServerDaemon() {
// We will use JSON to write the request data that will sent to the server
neroshop::Client * client = neroshop::Client::get_main_client();
int client_port = 1234;
int client_port = 40441;
std::string client_ip = "0.0.0.0";//"localhost";//0.0.0.0 means anyone can connect to your server
if(!client->connect(client_port, client_ip)) {
// free process
@ -1285,8 +1296,11 @@ void neroshop::Backend::connectToServerDaemon() {
}
//----------------------------------------------------------------
//----------------------------------------------------------------
void neroshop::Backend::testWriteJson() {
QJsonObject jsonObject; // base Object (required)
#include <nlohmann/json.hpp>
void neroshop::Backend::testWriteJson(const std::vector<std::string>& args) {
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
/*QJsonObject jsonObject; // base Object (required)
//QJsonObject json_attributes_object; // subObject (optional)
@ -1302,9 +1316,10 @@ void neroshop::Backend::testWriteJson() {
json_size_array.insert(json_size_array.size(), QJsonValue("L"));
json_size_array.insert(json_size_array.size(), QJsonValue("XL"));
/*jsonObject.insert*/jsonObject.insert(QString("weight"), QJsonValue(12.5));
/*jsonObject.insert*/jsonObject.insert(QString("color"), json_color_array);
/*jsonObject.insert*/jsonObject.insert(QString("size"), json_size_array);
//jsonObject.insert
jsonObject.insert(QString("weight"), QJsonValue(12.5));
jsonObject.insert(QString("color"), json_color_array);
jsonObject.insert(QString("size"), json_size_array);
//jsonObject.insert("attributes", json_attributes_object);
@ -1312,11 +1327,167 @@ void neroshop::Backend::testWriteJson() {
QJsonDocument doc(jsonObject);
QString strJson = doc.toJson(QJsonDocument::Compact); // https://doc.qt.io/qt-6/qjsondocument.html#JsonFormat-enum
// Display JSON as string
std::cout << strJson.toStdString() << std::endl;
std::cout << strJson.toStdString() << std::endl;*/
// Okay, so we (the server) have received a request from the client
std::string request = "{\n \"id\": \"2127323435\",\n \"jsonrpc\": \"2.0\",\n \"method\": \"query\",\n \"params\": {\n \"sql\": \"SELECT * FROM products WHERE condition = 'New' AND weight <= 0.0;\"\n }\n}";//"{\"id\":\"2127323435\",\"jsonrpc\":\"2.0\",\"method\":\"query\",\"params\":{\"1\":\"New\",\"2\":\"0.0\",\"count\":2,\"sql\":\"SELECT * FROM products WHERE condition = $1 AND weight <= $2;\"}}";
// First process the request ---------------------------------------
QJsonObject response_object; // JSON-RPC Response object
QJsonParseError parse_error;
const auto json_doc = QJsonDocument::fromJson(QString::fromStdString(request).toUtf8(), &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
neroshop::print("Error parsing client request", 1);
response_object.insert(QString("jsonrpc"), QJsonValue("2.0"));
// "result" MUST NOT exist if there was an error invoking the method
QJsonObject error_object; // "error" MUST be an Object as defined in section 5.1
error_object.insert(QString("code"), QJsonValue("-32700"));
error_object.insert(QString("message"), QJsonValue("Parse error"));
QString data;
switch(parse_error.error) {
case QJsonParseError::UnterminatedObject:
data = "Unterminated object";
break;
case QJsonParseError::MissingNameSeparator:
data = "Missing name separator";
break;
case QJsonParseError::UnterminatedArray:
data = "Unterminated array";
break;
case QJsonParseError::MissingValueSeparator:
data = "Missing value separator";
break;
case QJsonParseError::IllegalValue:
data = "Illegal value";
break;
case QJsonParseError::TerminationByNumber:
data = "Termination by number";
break;
case QJsonParseError::IllegalNumber:
data = "Illegal number";
break;
case QJsonParseError::IllegalEscapeSequence:
data = "Illegal escape sequence";
break;
case QJsonParseError::IllegalUTF8String:
data = "Illegal utf8 string";
break;
case QJsonParseError::UnterminatedString:
data = "Unterminated string";
break;
case QJsonParseError::MissingObject:
data = "Missing object";
break;
case QJsonParseError::DeepNesting:
data = "Deep nesting";
break;
case QJsonParseError::DocumentTooLarge:
data = "Document too large";
break;
case QJsonParseError::GarbageAtEnd:
data = "Garbage at end";
break;
}
if(!data.isEmpty()) error_object.insert(QString("data"), QJsonValue(data)); // additional information about the error which may be omitted
response_object.insert(QString("error"), QJsonValue(error_object));
response_object.insert(QString("id"), QJsonValue(QJsonValue::Null)); // "id" MUST be Null if there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request)
// TODO: return a response with an error_object
std::cout << "\033[91m" << QJsonDocument(response_object).toJson().toStdString() << "\033[0m\n";
return;
}
std::cout << "Request received:\n\033[33m" << json_doc.toJson().toStdString() << "\033[0m\n";
QJsonObject request_object = json_doc.object();
// Retrieve keys and values from request_object
QJsonValue jsonrpc_version = request_object.value("jsonrpc");
assert(jsonrpc_version.isString());
assert(jsonrpc_version.toString() == "2.0");
QJsonValue method = request_object.value("method");
assert(method.isString());
QJsonValue params = request_object.value("params"); // "params" MAY be omitted
QJsonValue id = request_object.value("id");
if(id.isUndefined()) {
// a request object without an "id" member is assumed to be NOTIFICATION ...
// a Notification signifies the Client's lack of interest in the corresponding Response object, and as such no Response object needs to be returned to the client. The Server MUST NOT reply to a Notification, including those that are within a batch request.
// exit function, with an empty string object perhaps
//return "";
}
// Execute the request (run function and get return value)
if(params.isObject()) {//if(params.isArray()) { //if(params.isUndefined()) {
if(method.toString() == "query") { std::cout << "method= query\n";
QJsonValue sql = params.toObject().value("sql");
assert(sql.isString());
std::vector<std::string> args;
QJsonValue arg_count = params.toObject().value("count");
if(!arg_count.isUndefined()) {
for(int index = 0; index < arg_count.toInt(); index++) {
QJsonValue argument = params.toObject().value(QString::fromStdString(std::to_string(index + 1)));
assert(argument.isString());
std::cout << argument.toString().toStdString() << " (arg: " << (index + 1) << ")" << std::endl;
args.push_back(argument.toString().toStdString()); // Store arguments in std::vector
}
assert(args.size() == arg_count.toInt()); // Make sure the number of args is the same as the count
// TODO: check whether sqlite method whether its a select or non-select statement
// select statements should use database->get_*params functions
database->execute_params(sql.toString().toStdString(), args); // Execute query with n args
}
else if(arg_count.isUndefined()) { // No args
// TODO: check whether sqlite method whether its a select or non-select statement
// select statements should use database->get_* functions
database->execute(sql.toString().toStdString()); // Execute query with zero args
}
}
//-------------------------------------------------------
else { std::cout << "method= " << method.toString().toStdString() << "\n";
//params.value("");
}
}
// After execution, get the result then return it as a JSON-RPC response message
////response_object.insert(QString("jsonrpc"), QJsonValue("2.0"));
/*
response_object.insert(QString("jsonrpc"), QJsonValue("2.0"));
response_object.insert(QString("result"), QJsonValue()); // "result" MUST NOT exist if there was an error invoking the method.
QJsonObject error_object; // "error" MUST be an Object as defined in section 5.1
//error_object.insert(QString("code"), QJsonValue());
//error_object.insert(QString("message"), QJsonValue());
//error_object.insert(QString("data"), QJsonValue());
response_object.insert(QString("error"), QJsonValue(error_object)); // "error" MUST NOT exist if there was no error triggered during invocation.
response_object.insert(QString("id"), QJsonValue(QString::fromStdString(random_id))); // "id" MUST be the same as the request object's id
// Convert JSON to string then display it (for debugging purposes)
QJsonDocument json_doc(response_object);
QString json_str = json_doc.toJson();////(QJsonDocument::Compact);
std::cout << json_str.toStdString() << "\n";*/
/*std::string command = "SELECT * FROM users;";
nlohmann::json json;
json["jsonrpc"] = "2.0";
json["method"] = "select";////get_method_type(command);
json["params"]["sql"] = command;
json["params"]["count"] = args.size();
for(int index = 0; index < args.size(); index++) {
std::string arg_key = std::to_string(index + 1);
std::string arg_value = args[index];
json["params"][arg_key] = arg_value;
}
// Generate random number for id (id can be either a string or an integer or null which is not recommended)
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr(1, 9);
std::string random_id;
for(int n = 0; n < 10; ++n) {
int random_integer = distr(gen);
random_id = random_id + std::to_string(random_integer);
}
json["id"] = random_id;
// Dump JSON to string
std::string request = json.dump(4);
#ifdef NEROSHOP_DEBUG
std::cout << request << std::endl;
#endif*/
}
//----------------------------------------------------------------
void neroshop::Backend::testfts5() {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
database->execute("CREATE VIRTUAL TABLE email USING fts5(sender, title, body);");

@ -47,7 +47,8 @@ public:
Q_INVOKABLE int getCategoryIdByName(const QString& category_name) const;
Q_INVOKABLE int getCategoryProductCount(int category_id) const; // returns number of products that fall under a specific category
Q_INVOKABLE QVariantList getMoneroNodeList() const;
Q_INVOKABLE QVariantList getNodeList(const QString& coin) const;
Q_INVOKABLE QVariantList getNodeListDefault(const QString& coin) const;
Q_INVOKABLE bool isWalletDaemonRunning() const;
QVariantList validateDisplayName(const QString& display_name) const; // Validates display name based on regex requirements
@ -86,7 +87,7 @@ public:
Q_INVOKABLE int getStockAvailable(const QString& product_id);
//Q_INVOKABLE void ();
static void testWriteJson();
static void testWriteJson(const std::vector<std::string>& args);
static void testfts5();
// Test function
static void startServerDaemon();

@ -1621,7 +1621,7 @@ QImage ImageLoader::load(const QString &id) const
std::cout << "failed to load image sorry\n";
}*/
// Load product image from file (TEXT) - crashes on certain images
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
std::string command = (!query.hasQueryItem(QString("image_id"))) ?
"SELECT name FROM images WHERE product_id = $1" : // use default image if key 'image_id' not found in query
@ -1638,7 +1638,7 @@ QImage ImageLoader::load(const QString &id) const
}
QPair<unsigned char*, int> ImageLoader::getProductImage(const QString &product_id) const {
neroshop::db::Sqlite3 * database = neroshop::db::Sqlite3::get_database();
neroshop::db::Sqlite3 * database = neroshop::get_database();
if(!database) throw std::runtime_error("database is NULL");
std::string command = "SELECT data FROM images WHERE product_id = $1";
sqlite3_stmt * statement = nullptr;

@ -14,8 +14,8 @@
using namespace neroshop;
static const QString WALLET_QR_PROVIDER {"wallet_qr"};
static const QString AVATAR_IMAGE_PROVIER {"avatar"};
static const QString CATALOG_IMAGE_PROVIER {"catalog"};
static const QString AVATAR_IMAGE_PROVIDER {"avatar"};
static const QString CATALOG_IMAGE_PROVIDER {"catalog"};
bool isIOS = false;
bool isAndroid = false;
@ -68,11 +68,11 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine;
//--------------------------
// start server daemon
//Backend::startServerDaemon();
/*Backend::startServerDaemon();
// wait for daemon server to open
//Backend::waitForServerDaemon();
Backend::waitForServerDaemon();
// connect to server daemon
//Backend::connectToServerDaemon();
Backend::connectToServerDaemon();*/
// Configuration file must be loaded right after Qt Application object has been created so that we can get the correct config location
// open configuration script
neroshop::open_configuration_file();
@ -85,7 +85,8 @@ int main(int argc, char *argv[])
// start database
Backend::initializeDatabase();
// testing
//Backend::testfts5();//Backend::testWriteJson();
//Backend::testfts5();//
Backend::testWriteJson({"New"});
// import paths
engine.addImportPath(":/fonts"); // import FontAwesome 1.0
// platform macros
@ -123,8 +124,8 @@ int main(int argc, char *argv[])
qRegisterMetaType<UserController *>();
engine.addImageProvider(WALLET_QR_PROVIDER, new WalletQrProvider(WALLET_QR_PROVIDER));
engine.addImageProvider(AVATAR_IMAGE_PROVIER, new ImageProvider(AVATAR_IMAGE_PROVIER));
engine.addImageProvider(CATALOG_IMAGE_PROVIER, new ImageProvider(CATALOG_IMAGE_PROVIER));
engine.addImageProvider(AVATAR_IMAGE_PROVIDER, new ImageProvider(AVATAR_IMAGE_PROVIDER));
engine.addImageProvider(CATALOG_IMAGE_PROVIDER, new ImageProvider(CATALOG_IMAGE_PROVIDER));
//--------------------------
// Load main.qml from the "qml/" directory
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));

@ -235,32 +235,40 @@ double neroshop::WalletController::getBalanceUnlocked(unsigned int account_index
QVariantList neroshop::WalletController::getTransfers() const {
if (!_wallet.get()) throw std::runtime_error("neroshop::Wallet is not initialized");
if (!_wallet->get_monero_wallet()) throw std::runtime_error("monero_wallet_full is not opened");
// TODO: make this function async or put in a separate thread
if (!_wallet.get())
throw std::runtime_error("neroshop::Wallet is not initialized");
if (!_wallet->get_monero_wallet())
throw std::runtime_error("monero_wallet_full is not opened");
double piconero = 0.000000000001;
monero_transfer_query transfer_query; // optional
auto transfers = _wallet->get_monero_wallet()->get_transfers(transfer_query);
QVariantList transfers_list;
for (auto transfer : transfers) { /*for(int i = 0; i < transfers.size(); i++) {
monero_transfer * transfer = transfers[i].get();*/
QVariantMap transfer_object;
transfer_object.insert("amount", (transfer->m_amount.get() * piconero));
transfer_object.insert("account_index", transfer->m_account_index.get()); // obviously account index 0
transfer_object.insert("is_incoming", transfer->is_incoming().get());
transfer_object.insert("is_outgoing", transfer->is_outgoing().get());
monero_tx_wallet * tx_wallet = transfer->m_tx.get();
////transfer_object.insert("", tx_wallet->);
//std::cout << ": " << tx_wallet-> << "\n";
std::packaged_task<QVariantList(void)> get_transfers_task([this]() -> QVariantList {
double piconero = 0.000000000001;
monero_transfer_query transfer_query; // optional
auto transfers = _wallet->get_monero_wallet()->get_transfers(transfer_query);
QVariantList transfers_list;
for (auto transfer : transfers) { /*for(int i = 0; i < transfers.size(); i++) {
monero_transfer * transfer = transfers[i].get();*/
QVariantMap transfer_object;
transfer_object.insert("amount", (transfer->m_amount.get() * piconero));
transfer_object.insert("account_index", transfer->m_account_index.get()); // obviously account index 0
transfer_object.insert("is_incoming", transfer->is_incoming().get());
transfer_object.insert("is_outgoing", transfer->is_outgoing().get());
monero_tx_wallet * tx_wallet = transfer->m_tx.get();
////transfer_object.insert("", tx_wallet->);
//std::cout << ": " << tx_wallet-> << "\n";
transfers_list.append(transfer_object);
}
return transfers_list;
transfers_list.append(transfer_object);
}
return transfers_list;
});
std::future<QVariantList> future_result = get_transfers_task.get_future();
// move the task (function) to a separate thread to prevent blocking of the main thread
std::thread worker(std::move(get_transfers_task));
worker.detach(); // join may block but detach won't
QVariantList transfers_result = future_result.get();
return transfers_result;
}

Loading…
Cancel
Save