Merge branch 'master' of git.snootgame.xyz:Cavemanon/cavecomm

This commit is contained in:
2023-04-02 21:14:58 -05:00
41 changed files with 584 additions and 1665 deletions

View File

@ -1,19 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="bin/main" path="src/main/java">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/java">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk-17.0.6"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

190
.gitignore vendored
View File

@ -1,186 +1,6 @@
### Vert.x ###
.vertx/
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
### Intellij+iml ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-buildTool-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-buildTool.properties
fabric.properties
### Intellij+iml Patch ###
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
## C++ shit
/bin
cavecomm
.idea/.gitignore
.idea/misc.xml
*.ipr
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
!/.mvn/wrapper/maven-wrapper.jar
### Gradle ###
.gradle
/buildTool/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
### NetBeans ###
nbproject/private/
buildTool/
nbbuild/
dist/
nbdist/
.nb-gradle/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.idea/
.idea/vcs.xml

View File

@ -6,12 +6,12 @@
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
@ -20,4 +20,15 @@
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
<filteredResources>
<filter>
<id>1678524602691</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@ -1,41 +0,0 @@
CREATE DATABASE cavecommdatabase;
\connect cavecommdatabase
CREATE SCHEMA public;
CREATE TABLE public.commissions (
requestid integer NOT NULL,
customeremailaddress text NOT NULL,
freelancer character varying(64) NOT NULL,
templatename character varying(64) DEFAULT 'N/A'::character varying NOT NULL,
currencypreference character varying(6) NOT NULL,
priceupfront numeric DEFAULT 0 NOT NULL,
priceondeliver numeric DEFAULT 0 NOT NULL,
requestdescription text DEFAULT 'N/A'::text NOT NULL,
accepted boolean,
upfrontinvoiceid character varying(64) DEFAULT 'N/A'::character varying NOT NULL,
ondeliverinvoiceid character varying(64) DEFAULT 'N/A'::character varying NOT NULL,
upfrontpaid boolean DEFAULT false NOT NULL,
ondeliverpaid boolean DEFAULT false NOT NULL
);
COMMENT ON COLUMN public.commissions.accepted IS 'null = undecided';
CREATE SEQUENCE public.commissions_requestid_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.commissions_requestid_seq OWNER TO testuser;
ALTER SEQUENCE public.commissions_requestid_seq OWNED BY public.commissions.requestid;
ALTER TABLE ONLY public.commissions ALTER COLUMN requestid SET DEFAULT nextval('public.commissions_requestid_seq'::regclass);
SELECT pg_catalog.setval('public.commissions_requestid_seq', 1, false);

27
Makefile Normal file
View File

@ -0,0 +1,27 @@
.POSIX:
INC=-I/home/user/project/resources/boost_1_81_0 -L/home/user/project/resources/boost_1_81_0/stage/lib
SRCFILES = src/main.cpp
db:
sudo -u postgres createuser -P -e cavecommadmin
sudo -u postgres createdb --owner=cavecommadmin cavecomm 'contains all the information for the cavecomm instance on this device'
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE cavecomm TO cavecommadmin;"
sudo -u postgres psql -c "CREATE TABLE requests( id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, customerEmailAddress text, freelancer text, templateName text, currencyPreference varchar(6), priceUpFront decimal, priceOnDeliver decimal, requestDescription text, accepted boolean, upFrontInvoiceID text, onDeliverInvoiceID text, upFrontPaid boolean, onDeliverPaid boolean); " cavecomm
sudo -u postgres psql -c "ALTER TABLE requests OWNER TO cavecommadmin" cavecomm
dbclean:
sudo -u postgres sh -c 'dropdb cavecomm ; dropuser cavecommadmin'
all:
rm -r bin cavecomm
g++ $(INC) -o cavecomm $(SRCFILES) -lpqxx -lfmt -pthread
build:
g++ $(INC) -o cavecomm $(SRCFILES) -lpqxx -lfmt -pthread
release:
mkdir bin
g++ $(INC) -o bin/cavecomm $(SRCFILES) -lpqxx -lfmt -pthread
clean:
rm -r bin cavecomm

View File

@ -1,32 +0,0 @@
= Starter
image:https://img.shields.io/badge/vert.x-4.3.8-purple.svg[link="https://vertx.io"]
This application was generated using http://start.vertx.io
== Building
To launch your tests:
```
./gradlew clean test
```
To package your application:
```
./gradlew clean assemble
```
To run your application:
```
./gradlew clean run
```
== Help
* https://vertx.io/docs/[Vert.x Documentation]
* https://stackoverflow.com/questions/tagged/vert.x?sort=newest&pageSize=15[Vert.x Stack Overflow]
* https://groups.google.com/forum/?fromgroups#!forum/vertx[Vert.x User Group]
* https://discord.gg/6ry7aqPWXy[Vert.x Discord]
* https://gitter.im/eclipse-vertx/vertx-users[Vert.x Gitter]

View File

@ -1,3 +1,11 @@
# cavecomm
Cavemanon's custom commissioning service
## Installation
Depends on:
https://packages.debian.org/bullseye/libpqxx-dev
https://packages.debian.org/bullseye/libfmt-dev
https://crowcpp.org
**TODO: Write this**

View File

@ -1,63 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.tasks.testing.logging.TestLogEvent.*
plugins {
java
application
id("com.github.johnrengelman.shadow") version "7.1.2"
}
group = "com.cavecomm"
version = "1.0.0-SNAPSHOT"
repositories {
mavenCentral()
}
val vertxVersion = "4.3.8"
val junitJupiterVersion = "5.9.1"
val mainVerticleName = "cavecomm.MainVerticle"
val launcherClassName = "io.vertx.core.Launcher"
val watchForChange = "src/**/*"
val doOnChange = "${projectDir}/gradlew classes"
application {
mainClass.set(launcherClassName)
}
dependencies {
implementation(platform("io.vertx:vertx-stack-depchain:$vertxVersion"))
implementation("io.vertx:vertx-web")
implementation("io.vertx:vertx-pg-client")
implementation("io.vertx:vertx-web-sstore-cookie")
implementation("io.vertx:vertx-auth-oauth2")
implementation("com.ongres.scram:client:2.1")
testImplementation("io.vertx:vertx-unit")
testImplementation("junit:junit:4.13.2")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.withType<ShadowJar> {
archiveClassifier.set("fat")
manifest {
attributes(mapOf("Main-Verticle" to mainVerticleName))
}
mergeServiceFiles()
}
tasks.withType<Test> {
useJUnit()
testLogging {
events = setOf(PASSED, SKIPPED, FAILED)
}
}
tasks.withType<JavaExec> {
args = listOf("run", mainVerticleName, "--redeploy=$watchForChange", "--launcher-class=$launcherClassName", "--on-redeploy=$doOnChange")
}

View File

@ -1,106 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
<title>Test results - Class cavecomm.TestMainVerticle</title>
<link href="../css/base-style.css" rel="stylesheet" type="text/css"/>
<link href="../css/style.css" rel="stylesheet" type="text/css"/>
<script src="../js/report.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<h1>Class cavecomm.TestMainVerticle</h1>
<div class="breadcrumbs">
<a href="../index.html">all</a> &gt;
<a href="../packages/cavecomm.html">cavecomm</a> &gt; TestMainVerticle</div>
<div id="summary">
<table>
<tr>
<td>
<div class="summaryGroup">
<table>
<tr>
<td>
<div class="infoBox" id="tests">
<div class="counter">1</div>
<p>tests</p>
</div>
</td>
<td>
<div class="infoBox" id="failures">
<div class="counter">0</div>
<p>failures</p>
</div>
</td>
<td>
<div class="infoBox" id="ignored">
<div class="counter">0</div>
<p>ignored</p>
</div>
</td>
<td>
<div class="infoBox" id="duration">
<div class="counter">0.689s</div>
<p>duration</p>
</div>
</td>
</tr>
</table>
</div>
</td>
<td>
<div class="infoBox success" id="successRate">
<div class="percent">100%</div>
<p>successful</p>
</div>
</td>
</tr>
</table>
</div>
<div id="tabs">
<ul class="tabLinks">
<li>
<a href="#tab0">Tests</a>
</li>
<li>
<a href="#tab1">Standard output</a>
</li>
</ul>
<div id="tab0" class="tab">
<h2>Tests</h2>
<table>
<thead>
<tr>
<th>Test</th>
<th>Duration</th>
<th>Result</th>
</tr>
</thead>
<tr>
<td class="success">verticle_deployed</td>
<td class="success">0.689s</td>
<td class="success">passed</td>
</tr>
</table>
</div>
<div id="tab1" class="tab">
<h2>Standard output</h2>
<span class="code">
<pre>HTTP server started on port 8888
</pre>
</span>
</div>
</div>
<div id="footer">
<p>
<div>
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
</label>
</div>Generated by
<a href="http://www.gradle.org">Gradle 7.3.2</a> at 02.03.2023, 19:15:42</p>
</div>
</div>
</body>
</html>

View File

@ -1,179 +0,0 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 12pt;
}
body, a, a:visited {
color: #303030;
}
#content {
padding-left: 50px;
padding-right: 50px;
padding-top: 30px;
padding-bottom: 30px;
}
#content h1 {
font-size: 160%;
margin-bottom: 10px;
}
#footer {
margin-top: 100px;
font-size: 80%;
white-space: nowrap;
}
#footer, #footer a {
color: #a0a0a0;
}
#line-wrapping-toggle {
vertical-align: middle;
}
#label-for-line-wrapping-toggle {
vertical-align: middle;
}
ul {
margin-left: 0;
}
h1, h2, h3 {
white-space: nowrap;
}
h2 {
font-size: 120%;
}
ul.tabLinks {
padding-left: 0;
padding-top: 10px;
padding-bottom: 10px;
overflow: auto;
min-width: 800px;
width: auto !important;
width: 800px;
}
ul.tabLinks li {
float: left;
height: 100%;
list-style: none;
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
padding-bottom: 5px;
margin-bottom: 0;
-moz-border-radius: 7px;
border-radius: 7px;
margin-right: 25px;
border: solid 1px #d4d4d4;
background-color: #f0f0f0;
}
ul.tabLinks li:hover {
background-color: #fafafa;
}
ul.tabLinks li.selected {
background-color: #c5f0f5;
border-color: #c5f0f5;
}
ul.tabLinks a {
font-size: 120%;
display: block;
outline: none;
text-decoration: none;
margin: 0;
padding: 0;
}
ul.tabLinks li h2 {
margin: 0;
padding: 0;
}
div.tab {
}
div.selected {
display: block;
}
div.deselected {
display: none;
}
div.tab table {
min-width: 350px;
width: auto !important;
width: 350px;
border-collapse: collapse;
}
div.tab th, div.tab table {
border-bottom: solid #d0d0d0 1px;
}
div.tab th {
text-align: left;
white-space: nowrap;
padding-left: 6em;
}
div.tab th:first-child {
padding-left: 0;
}
div.tab td {
white-space: nowrap;
padding-left: 6em;
padding-top: 5px;
padding-bottom: 5px;
}
div.tab td:first-child {
padding-left: 0;
}
div.tab td.numeric, div.tab th.numeric {
text-align: right;
}
span.code {
display: inline-block;
margin-top: 0em;
margin-bottom: 1em;
}
span.code pre {
font-size: 11pt;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
padding-right: 10px;
margin: 0;
background-color: #f7f7f7;
border: solid 1px #d0d0d0;
min-width: 700px;
width: auto !important;
width: 700px;
}
span.wrapped pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: break-all;
}
label.hidden {
display: none;
}

View File

@ -1,84 +0,0 @@
#summary {
margin-top: 30px;
margin-bottom: 40px;
}
#summary table {
border-collapse: collapse;
}
#summary td {
vertical-align: top;
}
.breadcrumbs, .breadcrumbs a {
color: #606060;
}
.infoBox {
width: 110px;
padding-top: 15px;
padding-bottom: 15px;
text-align: center;
}
.infoBox p {
margin: 0;
}
.counter, .percent {
font-size: 120%;
font-weight: bold;
margin-bottom: 8px;
}
#duration {
width: 125px;
}
#successRate, .summaryGroup {
border: solid 2px #d0d0d0;
-moz-border-radius: 10px;
border-radius: 10px;
}
#successRate {
width: 140px;
margin-left: 35px;
}
#successRate .percent {
font-size: 180%;
}
.success, .success a {
color: #008000;
}
div.success, #successRate.success {
background-color: #bbd9bb;
border-color: #008000;
}
.failures, .failures a {
color: #b60808;
}
.skipped, .skipped a {
color: #c09853;
}
div.failures, #successRate.failures {
background-color: #ecdada;
border-color: #b60808;
}
ul.linkList {
padding-left: 0;
}
ul.linkList li {
list-style: none;
margin-bottom: 5px;
}

View File

@ -1,133 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
<title>Test results - Test Summary</title>
<link href="css/base-style.css" rel="stylesheet" type="text/css"/>
<link href="css/style.css" rel="stylesheet" type="text/css"/>
<script src="js/report.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<h1>Test Summary</h1>
<div id="summary">
<table>
<tr>
<td>
<div class="summaryGroup">
<table>
<tr>
<td>
<div class="infoBox" id="tests">
<div class="counter">1</div>
<p>tests</p>
</div>
</td>
<td>
<div class="infoBox" id="failures">
<div class="counter">0</div>
<p>failures</p>
</div>
</td>
<td>
<div class="infoBox" id="ignored">
<div class="counter">0</div>
<p>ignored</p>
</div>
</td>
<td>
<div class="infoBox" id="duration">
<div class="counter">0.689s</div>
<p>duration</p>
</div>
</td>
</tr>
</table>
</div>
</td>
<td>
<div class="infoBox success" id="successRate">
<div class="percent">100%</div>
<p>successful</p>
</div>
</td>
</tr>
</table>
</div>
<div id="tabs">
<ul class="tabLinks">
<li>
<a href="#tab0">Packages</a>
</li>
<li>
<a href="#tab1">Classes</a>
</li>
</ul>
<div id="tab0" class="tab">
<h2>Packages</h2>
<table>
<thead>
<tr>
<th>Package</th>
<th>Tests</th>
<th>Failures</th>
<th>Ignored</th>
<th>Duration</th>
<th>Success rate</th>
</tr>
</thead>
<tbody>
<tr>
<td class="success">
<a href="packages/cavecomm.html">cavecomm</a>
</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0.689s</td>
<td class="success">100%</td>
</tr>
</tbody>
</table>
</div>
<div id="tab1" class="tab">
<h2>Classes</h2>
<table>
<thead>
<tr>
<th>Class</th>
<th>Tests</th>
<th>Failures</th>
<th>Ignored</th>
<th>Duration</th>
<th>Success rate</th>
</tr>
</thead>
<tbody>
<tr>
<td class="success">
<a href="classes/cavecomm.TestMainVerticle.html">cavecomm.TestMainVerticle</a>
</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0.689s</td>
<td class="success">100%</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="footer">
<p>
<div>
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
</label>
</div>Generated by
<a href="http://www.gradle.org">Gradle 7.3.2</a> at 02.03.2023, 19:15:42</p>
</div>
</div>
</body>
</html>

View File

@ -1,194 +0,0 @@
(function (window, document) {
"use strict";
var tabs = {};
function changeElementClass(element, classValue) {
if (element.getAttribute("className")) {
element.setAttribute("className", classValue);
} else {
element.setAttribute("class", classValue);
}
}
function getClassAttribute(element) {
if (element.getAttribute("className")) {
return element.getAttribute("className");
} else {
return element.getAttribute("class");
}
}
function addClass(element, classValue) {
changeElementClass(element, getClassAttribute(element) + " " + classValue);
}
function removeClass(element, classValue) {
changeElementClass(element, getClassAttribute(element).replace(classValue, ""));
}
function initTabs() {
var container = document.getElementById("tabs");
tabs.tabs = findTabs(container);
tabs.titles = findTitles(tabs.tabs);
tabs.headers = findHeaders(container);
tabs.select = select;
tabs.deselectAll = deselectAll;
tabs.select(0);
return true;
}
function getCheckBox() {
return document.getElementById("line-wrapping-toggle");
}
function getLabelForCheckBox() {
return document.getElementById("label-for-line-wrapping-toggle");
}
function findCodeBlocks() {
var spans = document.getElementById("tabs").getElementsByTagName("span");
var codeBlocks = [];
for (var i = 0; i < spans.length; ++i) {
if (spans[i].className.indexOf("code") >= 0) {
codeBlocks.push(spans[i]);
}
}
return codeBlocks;
}
function forAllCodeBlocks(operation) {
var codeBlocks = findCodeBlocks();
for (var i = 0; i < codeBlocks.length; ++i) {
operation(codeBlocks[i], "wrapped");
}
}
function toggleLineWrapping() {
var checkBox = getCheckBox();
if (checkBox.checked) {
forAllCodeBlocks(addClass);
} else {
forAllCodeBlocks(removeClass);
}
}
function initControls() {
if (findCodeBlocks().length > 0) {
var checkBox = getCheckBox();
var label = getLabelForCheckBox();
checkBox.onclick = toggleLineWrapping;
checkBox.checked = false;
removeClass(label, "hidden");
}
}
function switchTab() {
var id = this.id.substr(1);
for (var i = 0; i < tabs.tabs.length; i++) {
if (tabs.tabs[i].id === id) {
tabs.select(i);
break;
}
}
return false;
}
function select(i) {
this.deselectAll();
changeElementClass(this.tabs[i], "tab selected");
changeElementClass(this.headers[i], "selected");
while (this.headers[i].firstChild) {
this.headers[i].removeChild(this.headers[i].firstChild);
}
var h2 = document.createElement("H2");
h2.appendChild(document.createTextNode(this.titles[i]));
this.headers[i].appendChild(h2);
}
function deselectAll() {
for (var i = 0; i < this.tabs.length; i++) {
changeElementClass(this.tabs[i], "tab deselected");
changeElementClass(this.headers[i], "deselected");
while (this.headers[i].firstChild) {
this.headers[i].removeChild(this.headers[i].firstChild);
}
var a = document.createElement("A");
a.setAttribute("id", "ltab" + i);
a.setAttribute("href", "#tab" + i);
a.onclick = switchTab;
a.appendChild(document.createTextNode(this.titles[i]));
this.headers[i].appendChild(a);
}
}
function findTabs(container) {
return findChildElements(container, "DIV", "tab");
}
function findHeaders(container) {
var owner = findChildElements(container, "UL", "tabLinks");
return findChildElements(owner[0], "LI", null);
}
function findTitles(tabs) {
var titles = [];
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
var header = findChildElements(tab, "H2", null)[0];
header.parentNode.removeChild(header);
if (header.innerText) {
titles.push(header.innerText);
} else {
titles.push(header.textContent);
}
}
return titles;
}
function findChildElements(container, name, targetClass) {
var elements = [];
var children = container.childNodes;
for (var i = 0; i < children.length; i++) {
var child = children.item(i);
if (child.nodeType === 1 && child.nodeName === name) {
if (targetClass && child.className.indexOf(targetClass) < 0) {
continue;
}
elements.push(child);
}
}
return elements;
}
// Entry point.
window.onload = function() {
initTabs();
initControls();
};
} (window, window.document));

View File

@ -1,103 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
<title>Test results - Package cavecomm</title>
<link href="../css/base-style.css" rel="stylesheet" type="text/css"/>
<link href="../css/style.css" rel="stylesheet" type="text/css"/>
<script src="../js/report.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<h1>Package cavecomm</h1>
<div class="breadcrumbs">
<a href="../index.html">all</a> &gt; cavecomm</div>
<div id="summary">
<table>
<tr>
<td>
<div class="summaryGroup">
<table>
<tr>
<td>
<div class="infoBox" id="tests">
<div class="counter">1</div>
<p>tests</p>
</div>
</td>
<td>
<div class="infoBox" id="failures">
<div class="counter">0</div>
<p>failures</p>
</div>
</td>
<td>
<div class="infoBox" id="ignored">
<div class="counter">0</div>
<p>ignored</p>
</div>
</td>
<td>
<div class="infoBox" id="duration">
<div class="counter">0.689s</div>
<p>duration</p>
</div>
</td>
</tr>
</table>
</div>
</td>
<td>
<div class="infoBox success" id="successRate">
<div class="percent">100%</div>
<p>successful</p>
</div>
</td>
</tr>
</table>
</div>
<div id="tabs">
<ul class="tabLinks">
<li>
<a href="#tab0">Classes</a>
</li>
</ul>
<div id="tab0" class="tab">
<h2>Classes</h2>
<table>
<thread>
<tr>
<th>Class</th>
<th>Tests</th>
<th>Failures</th>
<th>Ignored</th>
<th>Duration</th>
<th>Success rate</th>
</tr>
</thread>
<tr>
<td class="success">
<a href="../classes/cavecomm.TestMainVerticle.html">TestMainVerticle</a>
</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0.689s</td>
<td class="success">100%</td>
</tr>
</table>
</div>
</div>
<div id="footer">
<p>
<div>
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
</label>
</div>Generated by
<a href="http://www.gradle.org">Gradle 7.3.2</a> at 02.03.2023, 19:15:42</p>
</div>
</div>
</body>
</html>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="cavecomm.TestMainVerticle" tests="1" skipped="0" failures="0" errors="0" timestamp="2023-03-02T18:15:41" hostname="KANAME_MADOKA" time="0.689">
<properties/>
<testcase name="verticle_deployed" classname="cavecomm.TestMainVerticle" time="0.689"/>
<system-out><![CDATA[HTTP server started on port 8888
]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>

View File

@ -1 +0,0 @@
"HTTP server started on port 8888

Binary file not shown.

View File

@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
gradlew vendored
View File

@ -1,185 +0,0 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
gradlew.bat vendored
View File

@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1 +0,0 @@
rootProject.name = "cavecomm"

47
setupdb.sh Normal file
View File

@ -0,0 +1,47 @@
#!/bin/sh
sudo -u postgres createuser -P -e cavecomm
sudo -u postgres createdb --owner=cavecomm cavecomm 'contains all the information for the cavecomm instance on this device'
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE cavecomm TO cavecomm;"
sudo -u postgres psql -c "CREATE TABLE requests(
id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
customerEmailAddress text,
freelancerID INT,
templateID INT,
currencyPreference varchar(6),
priceUpFront decimal,
priceOnDeliver decimal,
requestDescription text,
accepted boolean,
upFrontInvoiceID text,
onDeliverInvoiceID text,
upFrontPaid boolean,
onDeliverPaid boolean
);
" cavecomm
sudo -u postgres psql -c "CREATE TABLE freelancers(
id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
emailAddress text,
name text,
generalInformation text,
basicInformation text,
stripeAccountInformation text,
commissionLimit int
);
" cavecomm
sudo -u postgres psql -c "CREATE TABLE templates(
id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
freelancerID int,
name text,
content text
);
" cavecomm
sudo -u postgres psql -c "CREATE TABLE cryptoWallets(
id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
freelancerID int,
name text,
walletAddress text
);
" cavecomm

0
spec/buildchart.sh Executable file → Normal file
View File

View File

@ -97,7 +97,8 @@ TODO: Figure out what to do for this.
# HTML Templates
HTML Templates are the raw HTML that is used, and filled in, by variable data provided by the system. Templates should be wrote and saved on the file system of the hosting computer, and loaded upon start up. These loaded templates should be filled in with data provided by the system, then the assembled HTML should be given as a response to users.
HTML Templates are the raw HTML that is used, and filled in, by variable data provided by the system. Templates should be wrote and saved on the file system of the hosting computer, and loaded upon start up. These loaded templates should be filled in with data provided by the system, then the assembled HTML should be given as a response to users.
The Templates should utilize purely lower-case variable names.
# Payment Verification

5
src/README.md Normal file
View File

@ -0,0 +1,5 @@
Depends on:
https://packages.debian.org/bullseye/libpqxx-dev
https://packages.debian.org/bullseye/libfmt-dev
https://crowcpp.org

205
src/database.cpp Normal file
View File

@ -0,0 +1,205 @@
#include <string>
#include <list>
#include <iostream>
#include <vector>
#include <pqxx/pqxx>
#include "crow.h"
/*
* Database Manager
*/
namespace Database {
//Valid names for the JSON
const static std::string JSON_ITEM_NAMES[] = {"id", "customerEmailAddress", "freelancer", "templateName", "currencyPreference", "priceUpFront", "priceOnDeliver", "requestDescription", "accepted", "upFrontInvoiceID", "onDeliverInvoiceID", "upFrontPaid", "onDeliverPaid"};
/*
* Name and Statement for prepared statement to insert an item into Requests
* Takes the name of the freelancer and the template and resolves them to create the request
* todo:validate
* todo:createAlternative
*/
const static std::string PREPARED_STATEMENT_INSERT_ITEM_IN_REQUESTS = "insertItemInRequests";
const static std::string SQL_STATEMENT_INSERT_ITEM_IN_REQUESTS = "INSERT INTO requests(id, customerEmailAddress, freelancerid , templateid , currencyPreference, priceUpFront, priceOnDeliver, requestDescription, accepted, upFrontInvoiceID, onDeliverInvoiceID, upFrontPaid, onDeliverPaid) VALUES(DEFAULT, $1, (select id from freelancers where name = $2), (select id from templates where name = $3), $4, $5, $6, $7, $8, $9, $10, $11, $12 );";
/*
* Name and Statement for prepared statement to select an item based on a given ID
* Case for boolean return values because the default return value is f/t instead of 0/1 or false/true
* todo:validate
*/
const static std::string PREPARED_STATEMENT_SELECT_ITEM_BY_ID = "selectItemByID";
const static std::string SQL_STATEMENT_SELECT_ITEM_BY_ID = "SELECT requests.id , customerEmailAddress, freelancers.name, templates.name, currencyPreference, priceUpFront, priceOnDeliver, requestDescription, (CASE WHEN requests.accepted THEN 1 ELSE 0 END) AS accepted, upFrontInvoiceID, onDeliverInvoiceID, (CASE WHEN requests.upFrontPaid THEN 1 ELSE 0 END) AS upFrontPaid, (CASE WHEN requests.onDeliverPaid THEN 1 ELSE 0 END) AS onDeliverPaid FROM requests INNER JOIN freelancers ON freelancers.id=requests.freelancerID INNER JOIN templates ON templates.id=requests.templateID WHERE requests.id=$1;";
/*
* Name and Statement for prepared statement to select a freelancer based on a given id
*/
const static std::string PREPARED_STATEMENT_SELECT_FREELANCER = "selectFreelancer";
const static std::string SQL_STATEMENT_SELECT_FREELANCER = "select id, name, generalinformation, basicinformation from freelancers WHERE id=$1;";
/*
* Name and Statement for prepared statement to select a freelancers templates based on a given freelancer id
*/
const static std::string PREPARED_STATEMENT_SELECT_FREELANCER_TEMPLATES = "selectFreelancerTemplates";
const static std::string SQL_STATEMENT_SELECT_FREELANCER_TEMPLATES = "select id, name, content from templates where freelancerid = $1 order by id;";
/*
* Selects freelancers, their basicInfo and if the commissions are closed/open ordered by name.
* Delivers if the commission limit has been reached and if the commissions are closed based on the accepted/completed state of the freelancers requests
*/
const static std::string SQL_Statement_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE = "select id, name, basicInformation , (case when (commissionlimit <= (select count(*) as requestCount from requests where requests.accepted = true and requests.completed = false and requests.freelancerid = freelancers.id group by freelancers.id)) then 'Closed' else 'Open' end) as commissionsclosed from freelancers order by name;";
/*
* Struct Representing an item in Requests
*/
struct requestsItem {
int id;
std::string customerEmailAddress;
std::string freelancer;
std::string freelancerid;
std::string templateName;
std::string templateNameid;
std::string currencyPreference;
std::string priceUpFront;
std::string priceOnDeliver;
std::string requestDescription;
bool accepted;
std::string upFrontInvoiceID;
std::string onDeliverInvoiceID;
bool upFrontPaid;
bool onDeliverPaid;
/*
* Parses a JSON and fills the Item with the delivered values
*/
void insertJsonIntoItem(crow::json::rvalue itemJson){
if (itemJson.has(JSON_ITEM_NAMES[0])) id = itemJson[JSON_ITEM_NAMES[0]].i();
if (itemJson.has(JSON_ITEM_NAMES[1])) customerEmailAddress = itemJson[JSON_ITEM_NAMES[1]].s();
if (itemJson.has(JSON_ITEM_NAMES[2])) freelancer = itemJson[JSON_ITEM_NAMES[2]].s();
if (itemJson.has(JSON_ITEM_NAMES[3])) templateName = itemJson[JSON_ITEM_NAMES[3]].s();
if (itemJson.has(JSON_ITEM_NAMES[4])) currencyPreference = itemJson[JSON_ITEM_NAMES[4]].s();
if (itemJson.has(JSON_ITEM_NAMES[5])) priceUpFront = itemJson[JSON_ITEM_NAMES[5]].s();
if (itemJson.has(JSON_ITEM_NAMES[6])) priceOnDeliver = itemJson[JSON_ITEM_NAMES[6]].s();
if (itemJson.has(JSON_ITEM_NAMES[7])) requestDescription = itemJson[JSON_ITEM_NAMES[7]].s();
if (itemJson.has(JSON_ITEM_NAMES[8])) accepted = itemJson[JSON_ITEM_NAMES[8]].b();
if (itemJson.has(JSON_ITEM_NAMES[9])) upFrontInvoiceID = itemJson[JSON_ITEM_NAMES[9]].s();
if (itemJson.has(JSON_ITEM_NAMES[10])) onDeliverInvoiceID = itemJson[JSON_ITEM_NAMES[10]].s();
if (itemJson.has(JSON_ITEM_NAMES[11])) upFrontPaid = itemJson[JSON_ITEM_NAMES[11]].b();
if (itemJson.has(JSON_ITEM_NAMES[12])) onDeliverPaid = itemJson[JSON_ITEM_NAMES[12]].b();
}
};
/*
* Executes an SQL query and returns results
* Takes an open pqxx::connection
*/
pqxx::result executeSQL(pqxx::connection &connection, std::string sqlQuery) {
pqxx::work work(connection);
pqxx::result result = work.exec(sqlQuery);
work.commit();
return result;
}
/*
* Executes the prepared statement INSERT_ITEM_IN_REQUESTS
* Takes an open pqxx::connection and the requestsItem to insert
*/
pqxx::result executePreparedStatement_INSERT_ITEM_IN_REQUESTS(pqxx::connection &connection, requestsItem item) {
connection.prepare(PREPARED_STATEMENT_INSERT_ITEM_IN_REQUESTS, SQL_STATEMENT_INSERT_ITEM_IN_REQUESTS);
pqxx::work work(connection);
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_INSERT_ITEM_IN_REQUESTS,
item.customerEmailAddress, item.freelancer, item.templateName,
item.currencyPreference, item.priceUpFront, item.priceOnDeliver,
item.requestDescription, item.accepted, item.upFrontInvoiceID,
item.onDeliverInvoiceID, item.upFrontPaid, item.onDeliverPaid);
work.commit();
return result;
}
/*
* Executes the prepared statement SELECT_ITEM_BY_ID
* Takes an open pqxx::connection
*/
pqxx::result executeStatement_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE(pqxx::connection &connection) {
pqxx::work work(connection);
pqxx::result result = work.exec(SQL_Statement_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE);
work.commit();
return result;
}
/*
* Executes the prepared statement SELECT_ITEM_BY_ID
* Takes an open pqxx::connection and the id to select by
*/
pqxx::result executePreparedStatement_SELECT_ITEM_BY_ID(pqxx::connection &connection, int id) {
connection.prepare(PREPARED_STATEMENT_SELECT_ITEM_BY_ID, SQL_STATEMENT_SELECT_ITEM_BY_ID);
pqxx::work work(connection);
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_SELECT_ITEM_BY_ID, id);
work.commit();
return result;
}
/*
* Executes the prepared statement SELECT_FREELANCER
* Takes an open pqxx::connection and the id to select by
*/
pqxx::result executePreparedStatement_SELECT_FREELANCER(pqxx::connection &connection, int freelancerID) {
connection.prepare(PREPARED_STATEMENT_SELECT_FREELANCER, SQL_STATEMENT_SELECT_FREELANCER);
pqxx::work work(connection);
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_SELECT_FREELANCER, freelancerID);
work.commit();
return result;
}
/*
* Executes the prepared statement SELECT_FREELANCER_TEMPLATES
* Takes an open pqxx::connection and the id to select by
*/
pqxx::result executePreparedStatement_SELECT_FREELANCER_TEMPLATES(pqxx::connection &connection, int freelancerID) {
connection.prepare(PREPARED_STATEMENT_SELECT_FREELANCER_TEMPLATES, SQL_STATEMENT_SELECT_FREELANCER_TEMPLATES);
pqxx::work work(connection);
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_SELECT_FREELANCER_TEMPLATES, freelancerID);
work.commit();
return result;
}
/*
* parses the result and returns a JSON
* Takes a result and optionally the desired row
* !the result delivers only lowercase column names, either the templates are appropriately changed
* or a validation is performed at conversion.
*/
crow::json::wvalue convertResultRowToJSON(pqxx::result &result, int row = 0){
crow::json::wvalue returnJson;
for (int i = 0; i < result.columns(); ++i) {
returnJson[result.column_name(i)] = result.at(row).at(i).c_str();
}
return returnJson;
}
/*
* parses the result and returns a JSON
* parses it fully and returns a JSON containing it at the top or below a variable
* takes the result and optionally a name for the top level variable
*/
crow::json::wvalue convertResultToJSON(pqxx::result &result, std::string jsonName = ""){
std::vector<crow::json::wvalue> jsonVector;
for (int row = 0; row < result.size(); ++row) {
crow::json::wvalue jsonVectorItem;
for (int i = 0; i < result.columns(); ++i) {
jsonVectorItem[result.column_name(i)] = result.at(row).at(i).c_str();
}
jsonVector.push_back(jsonVectorItem);
}
crow::json::wvalue returnJson;
if (jsonName != ""){
returnJson[jsonName] = crow::json::wvalue(jsonVector);
}
else
{
returnJson = crow::json::wvalue(jsonVector);
}
return returnJson;
}
}

150
src/main.cpp Normal file
View File

@ -0,0 +1,150 @@
#include "crow.h"
#include <pqxx/pqxx>
#include <string>
#include <fmt/core.h>
#include "database.cpp"
using namespace std;
int main() {
crow::SimpleApp app;
// CROW_ROUTE(app, "/test.json")
// ([](){
// pqxx::connection db("postgresql://cavecommadmin:cavecomm@localhost:5432/cavecomm");
// pqxx::work work(db);
// pqxx::result result = work.exec("SELECT * from public.requests;");
// work.commit();
// return crow::json::wvalue x({{"message", rows[0][0].as<int>();}});
// });
string databaseURI = "postgresql://cavecommadmin:cavecomm@localhost:5432/cavecomm";
/*
* Freelancer Profile listing for customers
*/
CROW_ROUTE(app, "/")
([databaseURI]() {
pqxx::connection databaseConnection(databaseURI);
pqxx::result result = Database::executeStatement_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE(databaseConnection);
auto page = crow::mustache::load("customerIndex_FreelancerListing.html");
crow::json::wvalue resultJson = Database::convertResultToJSON(result, "freelancerProfiles");
crow::mustache::context ctx(resultJson);
return page.render(ctx);
});
/*
* Freelancer Profile Page for customers
*/
CROW_ROUTE(app, "/customer/<string>/<int>")
([databaseURI](string freelancerName, int freelancerID) {
pqxx::connection databaseConnection(databaseURI);
pqxx::result resultFreelancer = Database::executePreparedStatement_SELECT_FREELANCER(databaseConnection, freelancerID);
string freelancerHTML = "customer_FreelancerListing_NOTFOUND.html";
//crow::json::wvalue resultJsonFreelancer;
crow::json::wvalue resultJsonFreelancerTemplate;
if (resultFreelancer.size() != 0){
freelancerHTML = "customer_FreelancerListing.html";
//resultJsonFreelancer = Database::convertResultRowToJSON(resultFreelancer);
pqxx::result resultFreelancerTemplates = Database::executePreparedStatement_SELECT_FREELANCER_TEMPLATES(databaseConnection, freelancerID);
resultJsonFreelancerTemplate = Database::convertResultToJSON(resultFreelancerTemplates, "templates");
}
auto page = crow::mustache::load(freelancerHTML);
crow::mustache::context ctx(resultJsonFreelancerTemplate);
if (resultFreelancer.size() != 0){
/*
* puts freelancer data into context
* todo::solve this with less hardcoding
*/
for (int i = 0; i < resultFreelancer.columns(); ++i) {
string freelancerColumn = resultFreelancer.column_name(i);
ctx["freelancer" + freelancerColumn] = resultFreelancer.at(0).at(i).c_str();
}
}
ctx["freelancerid"] = freelancerID;
ctx["selectedfreelancername"] = freelancerName;
return page.render(ctx);
});
CROW_ROUTE(app, "/getEntry/<int>")
([databaseURI](int entry) {
pqxx::connection databaseConnection(databaseURI);
pqxx::result result = Database::executePreparedStatement_SELECT_ITEM_BY_ID(databaseConnection, entry);
auto page = crow::mustache::load("test.html");
crow::json::wvalue resultJson = Database::convertResultRowToJSON(result);
crow::mustache::context ctx(resultJson);
return page.render(ctx);
});
CROW_ROUTE(app, "/testRequest")
([databaseURI]() {
Database::requestsItem item;
item.customerEmailAddress = "test@testmail.com";
item.freelancer = "michael";
item.templateName = "coding";
item.currencyPreference = "XMR";
item.priceUpFront = "0.36";
item.priceOnDeliver = "0.36";
item.requestDescription = "This is a test";
item.accepted = stoi("0");
item.upFrontInvoiceID = "12424242424";
item.onDeliverInvoiceID = "329532532532";
item.upFrontPaid = stoi("0");
item.onDeliverPaid = stoi("0");
pqxx::connection databaseConnection(databaseURI);
Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, item);
return crow::response(200);
});
CROW_ROUTE(app, "/newRequest")
.methods("POST"_method)
([databaseURI](const crow::request &req) {
auto jsonBody = crow::json::load(req.body);
if (!jsonBody)
return crow::response(crow::status::BAD_REQUEST);
/*
* Example CURL to insert into DB:
* Price must be delivered as a string to avoid unnecessary conversion to double which would lead to the addition to a database of the value 0.359999999999999999999999999 instead of 0.36.
* curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"customerEmailAddress":"2@testmail.com","freelancer":"nick","templateName":"coding","currencyPreference":"XMR","priceUpFront":"0.36","priceOnDeliver":"0.36","requestDescription":"This is a test","accepted":false,"upFrontInvoiceID":"12424242424","onDeliverInvoiceID":"329532532532","upFrontPaid":false,"onDeliverPaid":false}' http://0.0.0.0:18080/newRequest
*/
Database::requestsItem item;
item.insertJsonIntoItem(jsonBody);
pqxx::connection databaseConnection(databaseURI);
Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, item);
return crow::response(200);
});
//set the port, set the app to run on multiple threads, and run the app
app.port(18080).multithreaded().run();
}

View File

@ -1,83 +0,0 @@
package cavecomm;
import io.vertx.core.Vertx;
import io.vertx.ext.web.RoutingContext;
import io.vertx.pgclient.PgConnectOptions;
import io.vertx.pgclient.PgPool;
import io.vertx.sqlclient.PoolOptions;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import io.vertx.sqlclient.SqlClient;
public class Database {
public static final int OUTPUT_DATATYPE_JSON = 0;
public static final int OUTPUT_DATATYPE_HTML = 1;
public static final int OUTPUT_DATATYPE_NONE = 2;
private final PgConnectOptions connectOptions;
private final PoolOptions poolOptions;
private final Vertx vertx;
private SqlClient client;
public Database(Vertx vertx, int port, String host, String database, String user, String password) {
this.vertx = vertx;
//Sets up the database Connection
connectOptions = new PgConnectOptions()
.setPort(port)
.setHost(host)
.setDatabase(database)
.setUser(user)
.setPassword(password);
poolOptions = new PoolOptions();
}
//Executes an SQL query
//requires further consideration of the Handling.
public void executeQuery(String query)
{
createClient();
client
.query(query)
.execute(ar -> {
if (ar.succeeded()) {
RowSet<Row> result = ar.result();
//test output
for (Row r : result) {
System.out.println("row: " + r.getString("testcolumn1"));
}
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
closeClient();
});
}
//Executes an SQL query while delivering a context to handle the data
//context output is a JSON
public void executeQuery(String query, RoutingContext rc, int outputDataType)
{
createClient();
client
.query(query)
.execute(ar -> {
switch (outputDataType) {
case OUTPUT_DATATYPE_JSON -> HandlerCollection.outputJSON(ar, rc);
case OUTPUT_DATATYPE_HTML -> HandlerCollection.outputHTML(ar, rc);
}
closeClient();
});
}
private void createClient()
{
client = PgPool.client(vertx, connectOptions, poolOptions);
}
private void closeClient()
{
System.out.println("Client Closing");
client.close();
}
}

View File

@ -1,47 +0,0 @@
package cavecomm;
import io.vertx.core.AsyncResult;
import io.vertx.core.json.JsonArray;
import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
//Class collecting Handlers to be used instead of Complex Lmabdas
public final class HandlerCollection {
private HandlerCollection() {
}
//returns a response containing the query result as a JSON
public static void outputJSON(AsyncResult<RowSet<Row>> ar, RoutingContext rc) {
if (ar.succeeded()) {
RowSet<Row> result = ar.result();
//dump RowSet into jsonArray
JsonArray jsonArray = new JsonArray();
for (Row r : result) {
jsonArray.add(r.toJson());
}
//sets the header to json
rc.response().putHeader("Content-Type", "application/json; charset=UTF8")
.end(jsonArray.encode());
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
}
//Potentially unnecessary to exist as its own handler
public static void outputHTML(AsyncResult<RowSet<Row>> ar, RoutingContext rc) {
if (ar.succeeded()) {
RowSet<Row> result = ar.result();
//dump RowSet into jsonArray
JsonArray jsonArray = new JsonArray();
for (Row r : result) {
jsonArray.add(r.toJson());
}
//sets the header to html
rc.response().putHeader("Content-Type", "text/html; charset=UTF8")
.end(jsonArray.encodePrettily());
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
}
}

View File

@ -1,77 +0,0 @@
package cavecomm;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.MultiMap;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
public class MainVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
//Template Engine / HTML Assembler
//
//TODO: actually make this work, for some reason io.vertx.ext.web.handler.TemplateEngine doesn't exist even though it does: https://vertx.io/docs/apidocs/io/vertx/ext/web/handler/package-summary.html
io.vertx.ext.web.handler.TemplateEngine engine = io.vertx.reactivex.ext.web.templ.freemarker.FreeMarkerTemplateEngine.create();
io.vertx.ext.web.handler.TemplateHandler templateHandler = io.vertx.ext.web.handler.TemplateHandler.create(engine);
// Create a Routeir
Router router = Router.router(vertx);
// Mount the handler for all incoming requests at every path and HTTP method
Database db = new Database(
vertx,
5432,
"localhost",
"testdb",
"testuser",
"123");
router.get("/jsonTest").handler(rc -> {
db.executeQuery("SELECT * FROM testtable", rc, Database.OUTPUT_DATATYPE_JSON);
}
);
router.get("/htmlTest").handler(rc -> {
db.executeQuery("SELECT * FROM testtable", rc, Database.OUTPUT_DATATYPE_HTML);
}
);
router.get("/dynamic/*").handler(templateHandler);
router.route().handler(context -> {
// Get the address of the request
String address = context.request().connection().remoteAddress().toString();
// Get the query parameter "name"
MultiMap queryParams = context.queryParams();
String name = queryParams.contains("name") ? queryParams.get("name") : "unknown";
// Write a json response
context.json(
new JsonObject()
.put("cavecomm", name)
.put("address", address)
.put("message", "Hello World")
);
});
// Create the HTTP server
vertx.createHttpServer()
// Handle every request using the router
.requestHandler(router)
// Start listening
.listen(8888)
// Print the port
.onSuccess(server ->
System.out.println(
"HTTP server started on port " + server.actualPort()
)
);
}
}

View File

@ -1,30 +0,0 @@
package cavecomm;
import io.vertx.core.Vertx;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(VertxUnitRunner.class)
public class TestMainVerticle {
@Rule
public RunTestOnContext rule = new RunTestOnContext();
@Before
public void deploy_verticle(TestContext testContext) {
Vertx vertx = rule.vertx();
vertx.deployVerticle(new MainVerticle(), testContext.asyncAssertSuccess());
}
@Test
public void verticle_deployed(TestContext testContext) throws Throwable {
Async async = testContext.async();
async.complete();
}
}

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<style>
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
</style>
</head>
<body>
<h2>Freelancer Profiles</h2>
<table>
<tr>
<th>Freelancer</th>
<th>Basic Information</th>
<th>Commission State</th>
</tr>
{{#freelancerProfiles}}
<tr>
<th><a href="/customer/{{name}}/{{id}}">{{name}}</a></th>
<th>{{basicinformation}}</th>
<th>{{commissionsclosed}}</th>
</tr>
{{/freelancerProfiles}}
</table>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<style>
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
</style>
</head>
<body>
<h2>Freelancer: {{freelancername}}</h2>
<div>Basic Profile:</div>
<div>{{freelancerbasicinformation}}</div>
<br>
<div>Detailed Profile:</div>
<div>{{freelancergeneralinformation}}</div>
<br>
<a href="/">Return to freelancer selection</a>
<br>
<table>
{{#templates}}
<tr>
<th><a href="/customer/{{freelancername}}/template/{{name}}/{{id}}">{{name}}</a></th>
<th>{{content}}</th>
</tr>
{{/templates}}
</table>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<style>
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
</style>
</head>
<body>
<h1>Freelancer: {{selectedfreelancername}} Could not be found!</h1>
<h2><a href="/">Return to freelancer selection</a></h2>
</body>
</html>

13
templates/test.html Normal file
View File

@ -0,0 +1,13 @@
<p>id: {{id}}</p>
<p>customerEmailAddress: {{customeremailaddress}}</p>
<p>freelancer: {{freelancer}}</p>
<p>templateName: {{templatename}}</p>
<p>currencyPreference: {{currencypreference}}</p>
<p>priceUpFront: {{priceupfront}}</p>
<p>priceOnDeliver: {{priceondeliver}}</p>
<p>requestDescription: {{requestdescription}}</p>
<p>upFrontInvoiceID: {{upfrontinvoiceid}}</p>
<p>onDeliverInvoiceID: {{ondeliverinvoiceid}}</p>
<p>upFrontPaid: {{upfrontpaid}}</p>
<p>onDeliverPaid: {{ondeliverpaid}}</p>
<p>accepted: {{accepted}}</p>