259 lines
8.8 KiB
Vue
259 lines
8.8 KiB
Vue
<script setup>
|
|
import { TabPanel} from '@headlessui/vue'
|
|
import { KeyIcon, BoltIcon, ShieldCheckIcon, UserPlusIcon, UsersIcon, ClipboardDocumentListIcon} from '@heroicons/vue/24/solid'
|
|
|
|
import Card from '@/components/cards/Card.vue';
|
|
import CardHeader from '@/components/cards/CardHeader.vue';
|
|
import CardTitle from '@/components/cards/CardTitle.vue';
|
|
import CardDescription from '@/components/cards/CardDescription.vue';
|
|
import CardContent from '@/components/cards/CardContent.vue';
|
|
import Label from '@/components/labels/Label.vue';
|
|
import InputText from '@/components/inputs/InputText.vue';
|
|
import Button from '@/components/buttons/Button.vue';
|
|
import CardFooter from '@/components/cards/CardFooter.vue';
|
|
import { ref } from 'vue';
|
|
import { getSocket } from '@/plugins/socketioManager'
|
|
|
|
import { onMounted, onUnmounted, onActivated, onDeactivated } from 'vue'
|
|
|
|
var socket = null
|
|
const publicKey = ref("")
|
|
const targetDifficulty = ref("4")
|
|
|
|
const lockboxServiceApiMineUrl = "/api/lockbox";
|
|
const tasks = ref(null)
|
|
|
|
// TODO Create api file
|
|
|
|
function start_mining(){
|
|
const data = {"public_key":publicKey.value, "force":targetDifficulty.value};
|
|
const requestOptions = {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data),
|
|
};
|
|
|
|
fetch(lockboxServiceApiMineUrl+"/start", requestOptions)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
|
|
})
|
|
.catch(error => {
|
|
console.error
|
|
|
|
('Error:', error);
|
|
});
|
|
}
|
|
|
|
function list_tasks(){
|
|
fetch(lockboxServiceApiMineUrl+"/list")
|
|
.then(response => {
|
|
if (!response.ok){
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
tasks.value = data;
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
});
|
|
}
|
|
|
|
function pause_task(taskId){
|
|
const data = {};
|
|
const requestOptions = {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data),
|
|
};
|
|
|
|
fetch(lockboxServiceApiMineUrl+"/pause/"+taskId, requestOptions)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
|
|
})
|
|
.catch(error => {
|
|
console.error
|
|
|
|
('Error:', error);
|
|
});
|
|
}
|
|
|
|
function resume_task(taskId){
|
|
const data = {};
|
|
const requestOptions = {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data),
|
|
};
|
|
|
|
fetch(lockboxServiceApiMineUrl+"/resume/"+taskId, requestOptions)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
|
|
})
|
|
.catch(error => {
|
|
console.error
|
|
|
|
('Error:', error);
|
|
});
|
|
}
|
|
|
|
function cancel_task(taskId){
|
|
const data = {};
|
|
const requestOptions = {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data),
|
|
};
|
|
|
|
fetch(lockboxServiceApiMineUrl+"/cancel/"+taskId, requestOptions)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
|
|
})
|
|
.catch(error => {
|
|
console.error
|
|
|
|
('Error:', error);
|
|
});
|
|
}
|
|
|
|
function onTaskUpdated(data){
|
|
console.log(data)
|
|
tasks.value[data.task_id] = data.result
|
|
}
|
|
|
|
onActivated(async () => {
|
|
list_tasks();
|
|
|
|
socket = await getSocket("lockbox_miner")
|
|
socket.on('taskUpdated', onTaskUpdated);
|
|
});
|
|
|
|
onDeactivated(()=>{
|
|
socket.off('taskUpdated', onTaskUpdated);
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<TabPanel class=" space-y-5">
|
|
<!-- Mining user -->
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle class="flex items-center gap-2">
|
|
<BoltIcon class="h-5 w-5" />
|
|
Proof of Work
|
|
</CardTitle>
|
|
<CardDescription class="text-slate-200">
|
|
Generate proof of work for a data
|
|
</CardDescription>
|
|
</CardHeader>
|
|
|
|
<CardContent>
|
|
<div class="flex flex-row w-full space-x-5 items-center justify-center text-center">
|
|
<div class="flex flex-col space-y-2 w-full">
|
|
<Label forId="publicKey" class="text-slate-300">Data</Label>
|
|
<div class="flex gap-2">
|
|
<InputText id="publicKey" v-model="publicKey" placeholder="" class="border-gray-700 bg-slate-300 w-full"/>
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-col space-y-2 w-full">
|
|
<Label forId="targetDifficulty" class="text-slate-300">Target Difficulty (Leading Zeros)</Label>
|
|
<div class="flex gap-2">
|
|
<InputText id="targetDifficulty" v-model="targetDifficulty" placeholder="" class="border-gray-700 bg-slate-300 w-full"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button @click="start_mining()" class="flex items-center gap-2 w-full">
|
|
Start Mining
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
<!-- Active jobs -->
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle class="flex items-center gap-2 text-yellow-400">
|
|
Active Jobs
|
|
</CardTitle>
|
|
<CardDescription class="text-slate-200">
|
|
Monitor and control proof of work jobs
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div class="overflow-y-auto rounded-md p-4">
|
|
<CardContent v-for="([taskId, task]) in Object.entries(tasks)" :key="taskId" class="border border-slate-300 rounded mb-2 pt-2">
|
|
<div class="grid md:grid-cols-5 gap-4 items-center">
|
|
<div class="flex flex-col">
|
|
<p class="text-sm text-gray-300 w-full">Data</p>
|
|
<p class=" text-gray-300">{{task.data}}</p>
|
|
</div>
|
|
<div class="flex flex-col">
|
|
<p class="text-sm text-gray-300 w-full">Difficulty</p>
|
|
<p class=" text-gray-300">{{task.best_force}}/{{task.target_force}}</p>
|
|
</div>
|
|
<div class="flex flex-col">
|
|
<p class="text-sm text-gray-300 w-full">Nonce</p>
|
|
<p class=" text-gray-300">{{task.best_nonce}}</p>
|
|
</div>
|
|
<div class="flex flex-col">
|
|
<p class="text-sm text-gray-300 w-full">Status</p>
|
|
<p :class="{
|
|
'text-yellow-400': task.status === 'paused',
|
|
'text-blue-400': task.status === 'running',
|
|
'text-green-400': task.status === 'completed',
|
|
'text-red-400': task.status === 'cancelled',
|
|
'text-gray-300': !['paused', 'running', 'completed', 'cancelled'].includes(task.status)
|
|
}">
|
|
{{ task.status }}
|
|
</p>
|
|
</div>
|
|
<div class="flex flex-row space-x-3 items-center mt-2">
|
|
<div v-if="!['completed', 'cancelled', 'error'].includes(task.status)" class="space-x-3">
|
|
<!-- TODO Change to Icons -->
|
|
<Button v-if="['running'].includes(task.status)" @click="pause_task(taskId)">Pause</Button>
|
|
<Button v-if="['paused'].includes(task.status)" @click="resume_task(taskId)">Play</Button>
|
|
<Button @click="cancel_task(taskId)">Cancel</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
</TabPanel>
|
|
</template> |