mirror of
https://github.com/gabehf/massflip.git
synced 2026-03-16 02:35:57 -07:00
0.0.4 rate limiting, captcha, more better auth, bug fixes
This commit is contained in:
parent
fdbf7217e9
commit
a3e56fa753
19 changed files with 363 additions and 95 deletions
36
frontend/package-lock.json
generated
36
frontend/package-lock.json
generated
|
|
@ -1,18 +1,19 @@
|
|||
{
|
||||
"name": "massflip",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "massflip",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"dependencies": {
|
||||
"core-js": "^3.8.3",
|
||||
"dotenv": "^16.0.1",
|
||||
"pinia": "^2.0.0-rc.10",
|
||||
"vue": "^3.2.13",
|
||||
"vue-gtag": "^2.0.1"
|
||||
"vue-gtag": "^2.0.1",
|
||||
"vue-recaptcha-v3": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
|
|
@ -9264,6 +9265,11 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recaptcha-v3": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/recaptcha-v3/-/recaptcha-v3-1.10.0.tgz",
|
||||
"integrity": "sha512-aGTxYSk3FFNKnXeKDbLpgRDRyIHRZNBF5HyaXXAN1Aj4TSyyZvmoAn9CylvpqLV3pYpIQavwc+2rzhNFn5SsLQ=="
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
|
|
@ -10788,6 +10794,17 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-recaptcha-v3": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-recaptcha-v3/-/vue-recaptcha-v3-2.0.1.tgz",
|
||||
"integrity": "sha512-isEDtOfHU4wWRrZZuxciAELtQmPOeEEdicPNa0f1AOyLPy3sCcBEcpFt+FOcO3RQv5unJ3Yn5NlsWtXv9rXqjg==",
|
||||
"dependencies": {
|
||||
"recaptcha-v3": "^1.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-style-loader": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||
|
|
@ -18387,6 +18404,11 @@
|
|||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"recaptcha-v3": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/recaptcha-v3/-/recaptcha-v3-1.10.0.tgz",
|
||||
"integrity": "sha512-aGTxYSk3FFNKnXeKDbLpgRDRyIHRZNBF5HyaXXAN1Aj4TSyyZvmoAn9CylvpqLV3pYpIQavwc+2rzhNFn5SsLQ=="
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
|
|
@ -19558,6 +19580,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"vue-recaptcha-v3": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-recaptcha-v3/-/vue-recaptcha-v3-2.0.1.tgz",
|
||||
"integrity": "sha512-isEDtOfHU4wWRrZZuxciAELtQmPOeEEdicPNa0f1AOyLPy3sCcBEcpFt+FOcO3RQv5unJ3Yn5NlsWtXv9rXqjg==",
|
||||
"requires": {
|
||||
"recaptcha-v3": "^1.8.0"
|
||||
}
|
||||
},
|
||||
"vue-style-loader": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "massflip",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
|
|
@ -12,7 +12,8 @@
|
|||
"dotenv": "^16.0.1",
|
||||
"pinia": "^2.0.0-rc.10",
|
||||
"vue": "^3.2.13",
|
||||
"vue-gtag": "^2.0.1"
|
||||
"vue-gtag": "^2.0.1",
|
||||
"vue-recaptcha-v3": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ onMounted(() => {
|
|||
let id = jar["session"]
|
||||
// handle logged in user
|
||||
let req = new XMLHttpRequest
|
||||
req.open("POST", "/api/login/bysession")
|
||||
req.open('POST', '/api/login/bysession')
|
||||
req.send(JSON.stringify({
|
||||
session: id
|
||||
}))
|
||||
|
|
@ -36,14 +36,16 @@ onMounted(() => {
|
|||
if (req.readyState == XMLHttpRequest.DONE) {
|
||||
let usr = JSON.parse(req.responseText)
|
||||
if ("error" in usr) {
|
||||
document.cookie = "session=; Max-Age=-99999999"
|
||||
console.log(usr["error"])
|
||||
document.cookie = 'session=; Max-Age=-99999999'
|
||||
console.log(usr['error'])
|
||||
return
|
||||
}
|
||||
userStore().updateUser(usr)
|
||||
let jar = cookiesToObj(cookieStr)
|
||||
let msg = JSON.stringify({
|
||||
type: "bind",
|
||||
username: usr.username
|
||||
type: "auth",
|
||||
username: usr.username,
|
||||
key: jar['session']
|
||||
})
|
||||
WSSend(msg)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ onMounted(() => {
|
|||
if (isNaN(hP)) {
|
||||
headsPercent.value = 0
|
||||
headsStyle['--p'] = 50
|
||||
|
||||
} else {
|
||||
headsPercent.value = hP
|
||||
headsStyle['--p'] = headsPercent.value
|
||||
|
|
@ -83,26 +84,30 @@ onMounted(() => {
|
|||
tailsPool.value = wsMsg.tailspool
|
||||
}
|
||||
WS.addEventListener("message", function (evt) {
|
||||
let wsMsg = JSON.parse(evt.data)
|
||||
if (wsMsg.type == "pool") {
|
||||
updatePool(wsMsg)
|
||||
} else if (wsMsg.type == "win") {
|
||||
userStore().addPoints(wsMsg.value)
|
||||
} else if (wsMsg.type == "tick") {
|
||||
let time = wsMsg.clock
|
||||
let timeString = (Math.floor(time/60)).toString() + ":" + ((time%60)>9?"":"0") + (time%60).toString() + "s"
|
||||
clock.value = timeString
|
||||
until.value = 'until next flip'
|
||||
updatePool(wsMsg)
|
||||
} else if (wsMsg.type == "flip") {
|
||||
clock.value = wsMsg.value
|
||||
until.value = ''
|
||||
userStore().setBet("")
|
||||
} else if (wsMsg.type == "hasbet") {
|
||||
if (wsMsg.value == true) {
|
||||
userStore().bet = wsMsg.bet
|
||||
let MSGs = evt.data.split('\n')
|
||||
MSGs.forEach((i) => {
|
||||
let wsMsg = JSON.parse(i)
|
||||
if (wsMsg.type == "pool") {
|
||||
updatePool(wsMsg)
|
||||
} else if (wsMsg.type == "win") {
|
||||
userStore().addPoints(wsMsg.value)
|
||||
} else if (wsMsg.type == "tick") {
|
||||
let time = wsMsg.clock
|
||||
let timeString = (Math.floor(time/60)).toString() + ":" + ((time%60)>9?"":"0") + (time%60).toString() + "s"
|
||||
clock.value = timeString
|
||||
until.value = 'until next flip'
|
||||
updatePool(wsMsg)
|
||||
} else if (wsMsg.type == "flip") {
|
||||
clock.value = wsMsg.value
|
||||
until.value = ''
|
||||
userStore().setBet("")
|
||||
} else if (wsMsg.type == "state") {
|
||||
if (wsMsg.value == true) {
|
||||
userStore().bet = wsMsg.bet
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -192,6 +197,9 @@ onMounted(() => {
|
|||
background:var(--c);
|
||||
transform:rotate(calc(var(--p)*3.6deg - 90deg)) translate(calc(var(--w)/2 - 50%));
|
||||
}
|
||||
@-moz-keyframes p{
|
||||
from{--p:0}
|
||||
}
|
||||
@keyframes p{
|
||||
from{--p:0}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,14 @@ function submitBet(HorT){
|
|||
if (userStore().bet != '') {
|
||||
return
|
||||
}
|
||||
if (bet.value <= 0) {
|
||||
if (bet.value <= 0 || isNaN(bet.value)) {
|
||||
hasError.value = true
|
||||
error.value = "Error: bet must be greater than 0"
|
||||
return
|
||||
} else if (bet.value % 1 != 0) {
|
||||
hasError.value = true
|
||||
error.value = "Error: bet must be a whole number"
|
||||
return
|
||||
}
|
||||
if (userStore().points - bet.value < 0) {
|
||||
hasError.value = true
|
||||
|
|
|
|||
|
|
@ -31,10 +31,11 @@ const ChatColors = {
|
|||
green: "limegreen",
|
||||
yellow: "gold",
|
||||
cyan: "cyan",
|
||||
red: "firebrick",
|
||||
red: "crimson",
|
||||
pink: "fuchsia",
|
||||
violet: "violet",
|
||||
orange: "orange",
|
||||
blue: "cornflowerblue"
|
||||
}
|
||||
|
||||
const _CHAT_MAX_HISTORY = 75;
|
||||
|
|
@ -49,28 +50,31 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
WS.addEventListener("message", function (evt) {
|
||||
let wsMsg = JSON.parse(evt.data)
|
||||
if (wsMsg.type == "chat") {
|
||||
chatQueue.enqueue(wsMsg)
|
||||
if (chatQueue.length >= _CHAT_MAX_HISTORY) {
|
||||
chatQueue.dequeue()
|
||||
let MSGs = evt.data.split('\n')
|
||||
MSGs.forEach((i) => {
|
||||
let wsMsg = JSON.parse(i)
|
||||
if (wsMsg.type == "chat") {
|
||||
chatQueue.enqueue(wsMsg)
|
||||
if (chatQueue.length >= _CHAT_MAX_HISTORY) {
|
||||
chatQueue.dequeue()
|
||||
}
|
||||
log.innerHTML = ""
|
||||
for (let message of Object.values(chatQueue.elements)) {
|
||||
var item = document.createElement("div")
|
||||
let fromUser = document.createElement("span")
|
||||
fromUser.style = `color: ${message.color};`
|
||||
fromUser.innerText = message.username
|
||||
item.appendChild(fromUser)
|
||||
let chatScore = document.createElement("span")
|
||||
chatScore.innerText = `(${message.points})`
|
||||
chatScore.style = `color: ${message.color};font-family: 'Helvetica';font-size: 12px;`
|
||||
item.appendChild(chatScore)
|
||||
let chatMsg = document.createTextNode(`: ${message.message}`)
|
||||
item.appendChild(chatMsg)
|
||||
appendLog(item)
|
||||
}
|
||||
}
|
||||
log.innerHTML = ""
|
||||
for (let message of Object.values(chatQueue.elements)) {
|
||||
var item = document.createElement("div")
|
||||
let fromUser = document.createElement("span")
|
||||
fromUser.style = `color: ${message.color};`
|
||||
fromUser.innerText = message.username
|
||||
item.appendChild(fromUser)
|
||||
let chatScore = document.createElement("span")
|
||||
chatScore.innerText = `(${message.points})`
|
||||
chatScore.style = `color: ${message.color};font-family: 'Helvetica';font-size: 12px;`
|
||||
item.appendChild(chatScore)
|
||||
let chatMsg = document.createTextNode(`: ${message.message}`)
|
||||
item.appendChild(chatMsg)
|
||||
appendLog(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@
|
|||
</div>
|
||||
<div id="noAcc" @click="toggleCreate">Don't have an account?</div>
|
||||
</div>
|
||||
<button class="submit" v-show="!tCreate" @click="login">Login</button>
|
||||
<button class="submit" v-show="tCreate" @click="createAccount">Create</button>
|
||||
<button class="submit" v-if="!tCreate" @click="login">Login</button>
|
||||
|
||||
<button class="submit" v-if="tCreate" @click="createAccount">Create</button>
|
||||
</form>
|
||||
<p id="serverResponse"> {{ serverResponse }}</p>
|
||||
</div>
|
||||
|
|
@ -26,6 +27,8 @@
|
|||
|
||||
<script setup>
|
||||
import { defineEmits, ref, reactive } from 'vue'
|
||||
import { useReCaptcha } from 'vue-recaptcha-v3'
|
||||
const { executeRecaptcha, recaptchaLoaded } = useReCaptcha()
|
||||
defineEmits(['display'])
|
||||
const form = reactive({
|
||||
username: '',
|
||||
|
|
@ -39,6 +42,11 @@ const pHasError = ref(false)
|
|||
const serverResponse = ref('')
|
||||
function toggleCreate() {
|
||||
tCreate.value = !tCreate.value
|
||||
if (tCreate.value) {
|
||||
document.getElementById("noAcc").innerText = "Already have an account?"
|
||||
} else {
|
||||
document.getElementById("noAcc").innerText = "Don't have an account?"
|
||||
}
|
||||
}
|
||||
function loginFieldsReady() {
|
||||
let ret = true
|
||||
|
|
@ -73,13 +81,17 @@ async function createAccount(e) {
|
|||
serverResponse.value = "passwords do not match"
|
||||
return
|
||||
}
|
||||
serverResponse.value = ""
|
||||
await recaptchaLoaded()
|
||||
const token = await executeRecaptcha('login')
|
||||
let req = new XMLHttpRequest()
|
||||
req.open("POST", "/api/createaccount")
|
||||
req.withCredentials = true
|
||||
req.send(JSON.stringify({
|
||||
username: form.username,
|
||||
password: form.password,
|
||||
remember: form.remember
|
||||
remember: form.remember,
|
||||
token: token
|
||||
}))
|
||||
req.onreadystatechange = () => {
|
||||
if (req.readyState == XMLHttpRequest.DONE) {
|
||||
|
|
@ -104,7 +116,7 @@ function login(e) {
|
|||
req.send(JSON.stringify({
|
||||
username: form.username,
|
||||
password: form.password,
|
||||
remember: form.remember
|
||||
remember: form.remember,
|
||||
}))
|
||||
req.onreadystatechange = () => {
|
||||
if (req.readyState == XMLHttpRequest.DONE) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<p>Massflip v0.0.3 - Copyright 2022 MNRVA</p>
|
||||
<p>Massflip v0.0.4 - Copyright 2022 MNRVA</p>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import { createApp } from 'vue'
|
|||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import VueGtag from "vue-gtag";
|
||||
import { VueReCaptcha } from 'vue-recaptcha-v3';
|
||||
|
||||
// switch these in production
|
||||
export const WS = new WebSocket("wss://" + "massflip.mnrva.dev" + "/ws")
|
||||
//export const WS = new WebSocket("ws://" + "127.0.0.1:8000" + "/ws")
|
||||
//export const WS = new WebSocket("ws://" + "localhost:8000" + "/ws")
|
||||
|
||||
WS.onclose = function() {
|
||||
alert("WebSocket connection closed.")
|
||||
|
|
@ -65,5 +66,8 @@ const pinia = createPinia()
|
|||
var app = createApp(App)
|
||||
app.use(pinia)
|
||||
app.use(VueGtag, {config: { id: "G-C3WQH98SZB" }})
|
||||
app.use(VueReCaptcha, { siteKey: '6LeDtKUgAAAAAH0OVNYPyxE8-k9EtjeSDW5jamle' }) // prod
|
||||
//app.use(VueReCaptcha, { siteKey: '6LfD6qUgAAAAAHCKSiEW1fuyuCJiZrAPya26Ro8Z' }) // dev
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ module.exports = defineConfig({
|
|||
module.exports = {
|
||||
devServer: {
|
||||
host: "localhost",
|
||||
port: 8000,
|
||||
proxy: {
|
||||
"/": {
|
||||
target: "http://localhost:8000",
|
||||
secure: false,
|
||||
ws: true
|
||||
secure: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue