Compare commits
77 Commits
6f18c4cb3c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
479044d4ea
|
|||
|
fd58f130a0
|
|||
|
4dcf065501
|
|||
|
75413b2c5e
|
|||
|
453f62cfae
|
|||
|
fde246ecb7
|
|||
|
46cc0237ce
|
|||
|
3506af77bf
|
|||
|
647e02d28e
|
|||
|
5bd5237bfa
|
|||
|
f829fb08d1
|
|||
|
d77439bafd
|
|||
|
3110d315e6
|
|||
|
507f6c932b
|
|||
|
58eda6d1ea
|
|||
|
e82adbe5e4
|
|||
|
01b3b7b429
|
|||
|
38f2f627a8
|
|||
|
feef4c7800
|
|||
|
1d04006335
|
|||
|
80e3d270c8
|
|||
|
58b3dfe28a
|
|||
|
758c87652c
|
|||
|
0bdaac368c
|
|||
|
31913d0b94
|
|||
|
fb9e3130e0
|
|||
|
2aaa448331
|
|||
|
fa8e4486f3
|
|||
|
20da46a8d7
|
|||
|
4a375149f6
|
|||
|
b02dfc0dac
|
|||
|
3e77074bec
|
|||
|
bf8594a546
|
|||
|
19110e74b4
|
|||
|
840d6e2ff5
|
|||
|
6ad1e9aa5c
|
|||
|
5d2aba9caa
|
|||
|
d5cae327f3
|
|||
|
02b69b8cdf
|
|||
|
8b1d66617f
|
|||
|
b26b0c01b0
|
|||
|
f8b9b5407b
|
|||
|
31a96558bf
|
|||
|
bba55bfc0e
|
|||
|
cfb643832b
|
|||
|
497c916d93
|
|||
|
8c07677a01
|
|||
|
677ac0241d
|
|||
|
d014848e71
|
|||
|
05d1952c84
|
|||
|
fae7507407
|
|||
|
f24dcae19f
|
|||
|
3761fc1b1e
|
|||
|
b704db1ba1
|
|||
|
b3d846c6f4
|
|||
|
f5f19f6dd4
|
|||
|
af171b3344
|
|||
|
3b9df825cd
|
|||
|
eb62c78624
|
|||
|
a85e63a327
|
|||
|
f0b94fb491
|
|||
|
48ec7f91bc
|
|||
|
a1fd8ea825
|
|||
|
95be6c0fca
|
|||
|
3666c4efbc
|
|||
|
561b81f2cf
|
|||
|
2d106ee612
|
|||
|
1b11b4ed7a
|
|||
|
77e847e81d
|
|||
|
2c0e3a1077
|
|||
|
60fa73286c
|
|||
|
8b17bc7294
|
|||
|
07d9de53fc
|
|||
|
34c71ee448
|
|||
|
c9d32454bd
|
|||
|
6e78a0ec88
|
|||
|
4e346d8113
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -126,7 +126,6 @@ dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
@@ -153,3 +152,9 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# custom
|
||||
|
||||
database/data.db
|
||||
|
||||
#
|
||||
.idea
|
||||
11
README.md
11
README.md
@@ -1,9 +1,8 @@
|
||||
# GiacPoints
|
||||
|
||||
A system for keeping track of house chores completed and attributing points to the activities.
|
||||
|
||||
# Functionality
|
||||
|
||||
Webpage - Login, add chores/activities (admin), tick of chores (user), log of completed chores (all)
|
||||
API layer - /loginUser, /getTasks, /completeTask, /getHistory?filter, /addTask
|
||||
Backend - DB, user (name password userid role), activities (taskid name points), history (userid taskid time pointsGained)
|
||||
# Installation
|
||||
`Note: Linux only`
|
||||
1. Clone this repo
|
||||
2. rename `database/example.db` to `database/data.db`
|
||||
3. Execute `main`
|
||||
BIN
database/example.db
Normal file
BIN
database/example.db
Normal file
Binary file not shown.
67
frontend/index.css
Normal file
67
frontend/index.css
Normal file
@@ -0,0 +1,67 @@
|
||||
.flex {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
|
||||
.loginSpacer {
|
||||
height: 33%;
|
||||
}
|
||||
|
||||
#login {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#content {
|
||||
height: 85%;
|
||||
width: 100%;
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
#menu {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: 5%;
|
||||
}
|
||||
#mainPage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.screen {
|
||||
width: 95%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#taskButton {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#historyContent {
|
||||
height: 75%;
|
||||
overflow: scroll;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#stats {
|
||||
height: 25%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#tasks {
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0;
|
||||
background: white;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#taskCard {
|
||||
margin-top: 45%;
|
||||
}
|
||||
70
frontend/index.html
Normal file
70
frontend/index.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>GiacPoints</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/picnic">
|
||||
<link rel="stylesheet" href="/frontend/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="login">
|
||||
<div class="flex three demo" style="height: 100%; width: 100%">
|
||||
<div class="fourth two-fifth-1000"></div>
|
||||
<div class="flex one demo half fifth-1000" style="height: 100%">
|
||||
<div class="loginSpacer"></div>
|
||||
<article class="card" style="padding: 5%">
|
||||
<label for="name">Name</label> <br>
|
||||
<input type="text" id="name"> <br>
|
||||
<label for="password">Password</label> <br>
|
||||
<input type="password" id="password"> <br>
|
||||
<input type="submit" value="Login" style="width: 100%" onclick="login()" id="loginSubmit"> <br>
|
||||
</article>
|
||||
<div class="loginSpacer"></div>
|
||||
</div>
|
||||
<div class="fourth two-fifth-1000"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mainPage" style="display: none;">
|
||||
<div id="content">
|
||||
<div id="tasks" class="screen flex two demo" style=""></div>
|
||||
<div id="history" class="screen" style="display: none;">
|
||||
<div id="stats">
|
||||
<article class="card">
|
||||
<header>
|
||||
<h1>Stats</h1>
|
||||
<pre id="statData"></pre>
|
||||
</header>
|
||||
</article>
|
||||
</div>
|
||||
<div id="historyContent"></div>
|
||||
</div>
|
||||
<div id="addTask" class="screen" style="display: none;">
|
||||
<article class="card" id="taskCard">
|
||||
<header>
|
||||
<h1>New task</h1> <br>
|
||||
<label for="newTaskName">Task name</label>
|
||||
<input type="text" id="newTaskName"> <br>
|
||||
<label for="newTaskPoints">Task points</label>
|
||||
<input type="number" id="newTaskPoints" min="1" max="5"> <br>
|
||||
<input onclick="newTask()" value="Create" type="submit"> <br>
|
||||
</header>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<div id="menu" class="flex three demo">
|
||||
<div class="third" id="taskButton">
|
||||
<span class="button" onclick="switchScreen('task')">Tasks</span>
|
||||
</div>
|
||||
<div class="third" id="historyButton">
|
||||
<span class="button" onclick="switchScreen('history')">History</span>
|
||||
</div>
|
||||
<div class="third" id="addButton">
|
||||
<span class="button tooltip-top" onclick="switchScreen('add')" >+ Task</span>
|
||||
<!-- <span class="button tooltip-top" disabled data-tooltip="In construction">+ Task</span>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/frontend/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
200
frontend/index.js
Normal file
200
frontend/index.js
Normal file
@@ -0,0 +1,200 @@
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function login() {
|
||||
// tell the user the app is loading
|
||||
document.getElementById("loginSubmit").value = "Loading";
|
||||
// this.preventDefault();
|
||||
// get login details
|
||||
let name = document.getElementById("name").value;
|
||||
let password = document.getElementById("password").value;
|
||||
// request login
|
||||
let response = await fetch("/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
password: password
|
||||
})
|
||||
});
|
||||
// if failed red button
|
||||
// else switch screen
|
||||
if (response.status !== 200) {
|
||||
document.getElementById("loginSubmit").classList.add("error");
|
||||
document.getElementById("loginSubmit").value = "Failed";
|
||||
return;
|
||||
} else {
|
||||
// switch screen
|
||||
document.getElementById("login").setAttribute("style", "display: none;");
|
||||
document.getElementById("mainPage").setAttribute("style", "");
|
||||
}
|
||||
|
||||
// store user data in session storage
|
||||
let data = await response.json();
|
||||
localStorage.setItem("uid", data.uid);
|
||||
localStorage.setItem("name", data.name);
|
||||
localStorage.setItem("role", data.role);
|
||||
|
||||
// if role is admin, keep auto styling
|
||||
if (localStorage.getItem("role") !== "admin") {
|
||||
document.getElementById("addButton").setAttribute("style", "display: none;");
|
||||
document.getElementById("taskButton").classList.value = "half";
|
||||
document.getElementById("historyButton").classList.value = "half";
|
||||
document.getElementById("menu").classList.value = "flex two";
|
||||
}
|
||||
|
||||
await populateScreen();
|
||||
}
|
||||
|
||||
async function populateScreen() {
|
||||
// get task data
|
||||
let taskPage = document.getElementById("tasks");
|
||||
taskPage.innerHTML = "";
|
||||
let response = await fetch("/getTasks");
|
||||
let tasksRaw = await response.json();
|
||||
let tasks = tasksRaw.tasks;
|
||||
|
||||
// get historical data
|
||||
let historyPage = document.getElementById("historyContent");
|
||||
historyPage.innerHTML = "";
|
||||
response = await fetch("/getHistory");
|
||||
let historyRaw = await response.json();
|
||||
let history = historyRaw.history;
|
||||
|
||||
// get points data
|
||||
let statsPage = document.getElementById("statData");
|
||||
statsPage.innerText = "";
|
||||
response = await fetch("/getUserPoints");
|
||||
let statsRaw = await response.json();
|
||||
let stats = statsRaw.userPoints;
|
||||
|
||||
// if it is a user make the tasks add points
|
||||
// or if admin make it do nothing
|
||||
if (localStorage.getItem("role") === "user") {
|
||||
try {
|
||||
for (let i = 0; i < tasks.length; i++) {
|
||||
taskPage.innerHTML += `
|
||||
<div id="${tasks[i].tid}" class="half" style="height: fit-content">
|
||||
<div class="button" onclick="completeTask(${tasks[i].tid})">
|
||||
<p id="taskName">${tasks[i].name}</p>
|
||||
<p id="${tasks[i].tid}-p">${tasks[i].points} points</p>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
} catch (e) {
|
||||
taskPage.innerHTML +=`<sub>such empty</sub>`
|
||||
}
|
||||
} else if (localStorage.getItem("role") === "admin") {
|
||||
try {
|
||||
for (let i = 0; i < tasks.length; i++) {
|
||||
taskPage.innerHTML += `
|
||||
<div id="${tasks[i].tid}" class="half" style="height: fit-content">
|
||||
<div class="button" disabled data-tooltip="Admins can't get points">
|
||||
<p id="taskName">${tasks[i].name}</p>
|
||||
<p id="${tasks[i].tid}-p">${tasks[i].points} points</p>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
} catch (e) {
|
||||
taskPage.innerHTML +=`<sub>such empty</sub>`
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (let i = (history.length -1); i >= 0; i-=1) {
|
||||
historyPage.innerHTML += `
|
||||
<div class="full">
|
||||
<article class="card">
|
||||
<header>
|
||||
<p>User: ${history[i].user}</p>
|
||||
<p>Task: ${history[i].task}</p>
|
||||
<p>Time: ${history[i].time}</p>
|
||||
<p>Points gained: ${history[i].pointsGained}</p>
|
||||
</header>
|
||||
</article>
|
||||
</div>`
|
||||
}
|
||||
} catch (e) {
|
||||
historyPage.innerHTML += `
|
||||
<sub>Such empty</sub>`
|
||||
}
|
||||
|
||||
for (let i = 0; i < stats.length; i++) {
|
||||
// put user points in box
|
||||
statsPage.innerText += `${stats[i].name}: ${stats[i].points} \n`
|
||||
}
|
||||
}
|
||||
|
||||
async function completeTask(taskID) {
|
||||
let points = document.getElementById(`${taskID}-p`).innerText.split(" ")[0];
|
||||
let time = new Date().toISOString().split(".")[0];
|
||||
let uid = localStorage.getItem("uid");
|
||||
await fetch("/completeTask", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
uid: Number(uid),
|
||||
tid: Number(taskID),
|
||||
time: time,
|
||||
pointsGained: Number(points)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async function switchScreen(button) {
|
||||
await populateScreen();
|
||||
document.getElementById("tasks").setAttribute("style", "display: none;");
|
||||
document.getElementById("history").setAttribute("style", "display: none;");
|
||||
document.getElementById("addTask").setAttribute("style", "display: none;");
|
||||
|
||||
if (button === "task") {
|
||||
document.getElementById("tasks").setAttribute("style", "");
|
||||
} else if (button === "history") {
|
||||
document.getElementById("history").setAttribute("style", "");
|
||||
} else if (button === "add") {
|
||||
document.getElementById("addTask").setAttribute("style", "");
|
||||
}
|
||||
}
|
||||
|
||||
async function newTask() {
|
||||
let points = document.getElementById(`newTaskPoints`);
|
||||
let name = document.getElementById(`newTaskName`);
|
||||
await fetch("/addTask", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tid: 0,
|
||||
name: name.value,
|
||||
points: Number(points.value),
|
||||
})
|
||||
});
|
||||
points.value = "";
|
||||
name.value = "";
|
||||
}
|
||||
|
||||
async function fastLog() {
|
||||
sleep(500);
|
||||
if (localStorage.getItem("uid") != null && localStorage.getItem("name") != null && localStorage.getItem("role") != null) {
|
||||
document.getElementById("login").setAttribute("style", "display: none;");
|
||||
document.getElementById("mainPage").setAttribute("style", "");
|
||||
if (localStorage.getItem("role") !== "admin") {
|
||||
document.getElementById("addButton").setAttribute("style", "display: none;");
|
||||
document.getElementById("taskButton").classList.value = "half";
|
||||
document.getElementById("historyButton").classList.value = "half";
|
||||
document.getElementById("menu").classList.value = "flex two";
|
||||
}
|
||||
|
||||
await populateScreen();
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
fastLog();
|
||||
39
go.mod
Normal file
39
go.mod
Normal file
@@ -0,0 +1,39 @@
|
||||
module giacPoints
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
100
go.sum
Normal file
100
go.sum
Normal file
@@ -0,0 +1,100 @@
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
279
main.go
Normal file
279
main.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// define global db
|
||||
var db, _ = sql.Open("sqlite3", "./database/data.db")
|
||||
|
||||
// types representing json queries
|
||||
|
||||
type loginInput struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type userData struct {
|
||||
UID int `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
Points int `json:"points"`
|
||||
}
|
||||
|
||||
type task struct {
|
||||
TID int `json:"tid"`
|
||||
Name string `json:"name"`
|
||||
Points int `json:"points"`
|
||||
}
|
||||
|
||||
type taskArray struct {
|
||||
Tasks []task `json:"tasks"`
|
||||
}
|
||||
|
||||
type newHistoryData struct {
|
||||
UID int `json:"uid"`
|
||||
TID int `json:"tid"`
|
||||
Time string `json:"time"`
|
||||
PointsGained int `json:"pointsGained"`
|
||||
}
|
||||
|
||||
type historyData struct {
|
||||
User string `json:"user"`
|
||||
Task string `json:"task"`
|
||||
Time string `json:"time"`
|
||||
PointsGained int `json:"pointsGained"`
|
||||
}
|
||||
|
||||
type historyArray struct {
|
||||
History []historyData `json:"history"`
|
||||
}
|
||||
|
||||
type userPointsData struct {
|
||||
Name string `json:"name"`
|
||||
Points int `json:"points"`
|
||||
}
|
||||
|
||||
type userPointsArray struct {
|
||||
UserPoints []userPointsData `json:"userPoints"`
|
||||
}
|
||||
|
||||
// log the user into their account
|
||||
func login(c *gin.Context) {
|
||||
// get the username and password from the request
|
||||
var requestedUser loginInput
|
||||
if err := c.BindJSON(&requestedUser); err != nil {
|
||||
c.IndentedJSON(http.StatusBadRequest, requestedUser)
|
||||
return
|
||||
}
|
||||
// check for user using given credentials
|
||||
stmt, err := db.Prepare("SELECT * FROM users WHERE name=?")
|
||||
checkErr(err)
|
||||
defer stmt.Close()
|
||||
var user userData
|
||||
err = stmt.QueryRow(requestedUser.Name).Scan(&user.UID, &user.Name, &user.Password, &user.Role, &user.Points)
|
||||
if err != nil {
|
||||
// search failed user not real
|
||||
c.IndentedJSON(http.StatusNotFound, requestedUser)
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
if user.Password != requestedUser.Password {
|
||||
// user not real
|
||||
c.IndentedJSON(http.StatusNotFound, requestedUser)
|
||||
panic(err)
|
||||
return
|
||||
} else {
|
||||
// user is in
|
||||
c.IndentedJSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getTasks(c *gin.Context) {
|
||||
// return a list of all the tasks
|
||||
var tasks []task
|
||||
// get array of all tasks
|
||||
rows, err := db.Query("SELECT * FROM activities")
|
||||
checkErr(err)
|
||||
for rows.Next() {
|
||||
var tempTask task
|
||||
err = rows.Scan(&tempTask.TID, &tempTask.Name, &tempTask.Points)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotFound, tasks)
|
||||
return
|
||||
}
|
||||
tasks = append(tasks, tempTask)
|
||||
}
|
||||
rows.Close()
|
||||
var jsonTasks taskArray
|
||||
jsonTasks.Tasks = tasks
|
||||
c.IndentedJSON(http.StatusOK, jsonTasks)
|
||||
}
|
||||
|
||||
func completeTask(c *gin.Context) {
|
||||
// get the task data from the request
|
||||
var completedTask newHistoryData
|
||||
if err := c.BindJSON(&completedTask); err != nil {
|
||||
c.IndentedJSON(http.StatusBadRequest, completedTask)
|
||||
return
|
||||
}
|
||||
// insert data to history table
|
||||
stmt, err := db.Prepare("INSERT INTO history (uid, taskid, time, pointsGained) VALUES (?, ?, ?, ?)")
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotModified, completedTask)
|
||||
return
|
||||
}
|
||||
_, err = stmt.Exec(completedTask.UID, completedTask.TID, completedTask.Time, completedTask.PointsGained)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotModified, completedTask)
|
||||
return
|
||||
}
|
||||
|
||||
// add points to user table
|
||||
stmt, err = db.Prepare("UPDATE users SET points = points + ? WHERE uid=?")
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotModified, completedTask)
|
||||
return
|
||||
}
|
||||
_, err = stmt.Exec(completedTask.PointsGained, completedTask.UID)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotModified, completedTask)
|
||||
return
|
||||
}
|
||||
|
||||
c.IndentedJSON(http.StatusOK, completedTask)
|
||||
}
|
||||
|
||||
func getHistory(c *gin.Context) {
|
||||
// get the log of past points gained
|
||||
var history []newHistoryData
|
||||
// get array of all history Data
|
||||
rows, err := db.Query("SELECT * FROM history")
|
||||
checkErr(err)
|
||||
for rows.Next() {
|
||||
var tempHistoryData newHistoryData
|
||||
err = rows.Scan(&tempHistoryData.UID, &tempHistoryData.TID, &tempHistoryData.Time, &tempHistoryData.PointsGained)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotFound, history)
|
||||
return
|
||||
}
|
||||
history = append(history, tempHistoryData)
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
var historyRes []historyData
|
||||
// make the data human-readable
|
||||
for i := 0; i < len(history); i++ {
|
||||
var tempHistory historyData
|
||||
// get the username
|
||||
var tempUser userData
|
||||
stmt, err := db.Prepare("SELECT * FROM users WHERE uid=?")
|
||||
checkErr(err)
|
||||
err = stmt.QueryRow(history[i].UID).Scan(&tempUser.UID, &tempUser.Name, &tempUser.Password, &tempUser.Role, &tempUser.Points)
|
||||
checkErr(err)
|
||||
tempHistory.User = tempUser.Name
|
||||
stmt.Close()
|
||||
|
||||
// get the task name and points
|
||||
var tempTask task
|
||||
stmt, err = db.Prepare("SELECT * FROM activities WHERE taskId=?")
|
||||
checkErr(err)
|
||||
err = stmt.QueryRow(history[i].TID).Scan(&tempTask.TID, &tempTask.Name, &tempTask.Points)
|
||||
checkErr(err)
|
||||
tempHistory.Task = tempTask.Name
|
||||
tempHistory.PointsGained = tempTask.Points
|
||||
stmt.Close()
|
||||
|
||||
tempHistory.Time = history[i].Time
|
||||
|
||||
historyRes = append(historyRes, tempHistory)
|
||||
}
|
||||
|
||||
var jsonHistory historyArray
|
||||
jsonHistory.History = historyRes
|
||||
c.IndentedJSON(http.StatusOK, jsonHistory)
|
||||
}
|
||||
|
||||
func addTask(c *gin.Context) {
|
||||
// get the data of the new task
|
||||
var newTask task
|
||||
if err := c.BindJSON(&newTask); err != nil {
|
||||
c.IndentedJSON(http.StatusBadRequest, newTask)
|
||||
return
|
||||
}
|
||||
// insert new task into the task table
|
||||
stmt, err := db.Prepare("INSERT INTO activities (name, points) VALUES (?,?)")
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotModified, newTask)
|
||||
return
|
||||
}
|
||||
_, err = stmt.Exec(newTask.Name, newTask.Points)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotModified, newTask)
|
||||
return
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, newTask)
|
||||
}
|
||||
|
||||
func getUserPoints(c *gin.Context) {
|
||||
// get the names of all users
|
||||
var allUsers []userPointsData
|
||||
|
||||
rows, err := db.Query("SELECT name, points FROM users WHERE role='user'")
|
||||
checkErr(err)
|
||||
|
||||
// put them in an array of users
|
||||
for rows.Next() {
|
||||
var tempUser userPointsData
|
||||
err = rows.Scan(&tempUser.Name, &tempUser.Points)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusNotFound, allUsers)
|
||||
return
|
||||
}
|
||||
allUsers = append(allUsers, tempUser)
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
// return to the requester
|
||||
var jsonUserPoints userPointsArray
|
||||
jsonUserPoints.UserPoints = allUsers
|
||||
c.IndentedJSON(http.StatusOK, jsonUserPoints)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var port string
|
||||
flag.StringVar(&port, "port", "localhost:8080", "define the port you want the program to use to serve")
|
||||
flag.Parse()
|
||||
//release mode
|
||||
//gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.Default()
|
||||
|
||||
// api routes
|
||||
router.POST("/login", login)
|
||||
router.POST("/completeTask", completeTask)
|
||||
router.POST("/addTask", addTask)
|
||||
router.GET("/getTasks", getTasks)
|
||||
router.GET("/getHistory", getHistory)
|
||||
router.GET("/getUserPoints", getUserPoints)
|
||||
|
||||
// page routes
|
||||
router.LoadHTMLGlob("frontend/*")
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{})
|
||||
})
|
||||
router.Static("/frontend", "./frontend")
|
||||
|
||||
router.Run(port)
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user