Added libs
This commit is contained in:
2
rendezvous/.gitignore
vendored
Normal file
2
rendezvous/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
rendezvous.zip
|
||||
1
rendezvous/.mtimes.json
Normal file
1
rendezvous/.mtimes.json
Normal file
@@ -0,0 +1 @@
|
||||
{".gitignore": 1741162518.7421248, "api.http": 1750144932.0607514, "config.json": 1756719481.3836672, "pseudoRendezvousServer.py": 1752094306.8508055, "rendezvousClient.py": 1757083644.9333432, "rendezvousClientApiBlueprint.py": 1756712048.0965717, "rendezvousClientSocketio.py": 1756719301.4128492, "rendezvousServer.py": 1757082996.803643, "rendezvousServerApiBlueprint.py": 1757831969.294421, "vue\\router.js": 1757837897.6922421, "vue\\api\\api.js": 1757832163.1491134, "vue\\components\\ClientTab.vue": 1757830119.6542766, "vue\\components\\ServerTab.vue": 1766701237.041469, "vue\\views\\HomeView.vue": 1757832140.9252956}
|
||||
18
rendezvous/api.http
Normal file
18
rendezvous/api.http
Normal file
@@ -0,0 +1,18 @@
|
||||
@hostname = http://localhost
|
||||
@port = 5050
|
||||
@hostServer = {{hostname}}:{{port}}/rendezvousServer
|
||||
@hostClient = {{hostname}}:{{port}}/rendezvousClient
|
||||
|
||||
###
|
||||
|
||||
GET {{hostServer}}/run
|
||||
|
||||
###
|
||||
|
||||
GET {{hostClient}}/server
|
||||
|
||||
###
|
||||
POST {{hostServer}}/server HTTP/1.1
|
||||
content-type: application/json
|
||||
|
||||
{"user":"A14/Rek5z78U1rYC+WLvU/ifnsX43o0tjnexmYdlXsjY"}
|
||||
6
rendezvous/config.json
Normal file
6
rendezvous/config.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"server": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 40441
|
||||
}
|
||||
}
|
||||
15
rendezvous/info.json
Normal file
15
rendezvous/info.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"id": "rendezvous",
|
||||
"version": 0.021,
|
||||
"modules": [
|
||||
{
|
||||
"id": "rendezvousClient",
|
||||
"version": 0
|
||||
},
|
||||
{
|
||||
"id": "rendezvousServer",
|
||||
"version": 0
|
||||
}
|
||||
],
|
||||
"frontend": "vue"
|
||||
}
|
||||
28
rendezvous/pseudoRendezvousServer.py
Normal file
28
rendezvous/pseudoRendezvousServer.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from libs.fspn.protocol.server import Server
|
||||
from libs.fspn.protocol.connection import Connection, EVENTS as CONNECTION_EVENTS
|
||||
from libs.zecho.interfaces.module import Module, ZECHO_EVENTS
|
||||
from libs.app.utils import get_logger
|
||||
|
||||
import logging
|
||||
import random
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
class PseudoRendezvousServer(Module):
|
||||
def __init__(self, zecho):
|
||||
super().__init__(zecho)
|
||||
self.zecho.subscribe_event("MESSAGE_"+self.module_name, self.on_message)
|
||||
|
||||
def connect_to_server(self, address):
|
||||
self.zecho.connect(address)
|
||||
|
||||
def on_connection_module(self, event):
|
||||
logger.debug('New Connection with module', event.source.address)
|
||||
# self.connections.append(event.source)
|
||||
|
||||
def on_message(self, event):
|
||||
logger.debug('Pseudo Rendezvous message: ', event.__dict__)
|
||||
|
||||
|
||||
|
||||
|
||||
81
rendezvous/rendezvousClient.py
Normal file
81
rendezvous/rendezvousClient.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import ipaddress
|
||||
|
||||
from libs.noSys.noSysModule import NoSysModule
|
||||
from libs.noSys.events import Events as nosys_events, DynamicEvents as nosys_dynamic_events
|
||||
from libs.noSys.peers import Peer
|
||||
from .rendezvousClientApiBlueprint import Blueprint
|
||||
from .rendezvousClientSocketio import HandlerSocketio
|
||||
from libs.app.common.logging import get_logger
|
||||
|
||||
logger = get_logger()
|
||||
# TODO Constants file
|
||||
RENDEZVOUS_SERVER_MODULE = ("rendezvous", "rendezvousServer")
|
||||
|
||||
class RendezvousClientEvents:
|
||||
SERVER_CONNECTED = "server_connected"
|
||||
SERVER_CONNECTION_ERROR = "server_connection_error"
|
||||
SERVER_DISCONNECTED = "server_disconnected"
|
||||
|
||||
# TODO PMC class inheritance
|
||||
class RendezvousClient(NoSysModule):
|
||||
def __init__(self, nosys_core):
|
||||
super().__init__(nosys_core)
|
||||
self.connections:dict[str, Peer] = {}
|
||||
|
||||
def setup(self):
|
||||
self.nosys_core.modules.api.register_blueprint(Blueprint(self).blueprint)
|
||||
self.nosys_core.modules.api.register_socketio(HandlerSocketio(self))
|
||||
|
||||
self.nosys_core.subscribe_event(nosys_dynamic_events.module_connection(RENDEZVOUS_SERVER_MODULE[0], RENDEZVOUS_SERVER_MODULE[1]), self.on_peer_module_server)
|
||||
|
||||
def on_nosys_ready(self, event):
|
||||
pass
|
||||
|
||||
def connect_to_server(self, address, user_id):
|
||||
peer, connect = self.nosys_core.connections.create_connection(address, user_id)
|
||||
self.connections[peer.id] = peer
|
||||
self.nosys_core.subscribe_event(nosys_dynamic_events.peer_connection(peer.id), self.on_server_connection)
|
||||
self.nosys_core.subscribe_event(nosys_dynamic_events.peer_connection_error(peer.id), self.on_server_connection_error)
|
||||
self.nosys_core.subscribe_event(nosys_dynamic_events.peer_disconnection(peer.id), self.on_server_disconnection)
|
||||
connect()
|
||||
return peer
|
||||
|
||||
def on_peer_module_server(self, event):
|
||||
peer:Peer = event.peer
|
||||
if peer.id in self.connections:
|
||||
print("SERVER CONNECTED")
|
||||
else:
|
||||
print("RANDOM SERVER CONNECTED")
|
||||
self.connections[peer.id] = peer
|
||||
|
||||
def on_module_connection_server(self, event):
|
||||
pass
|
||||
|
||||
def on_module_message(self, event):
|
||||
handler_action = getattr(self, 'on_'+event.data['action'])
|
||||
handler_action(event)
|
||||
|
||||
def send_get_random_peer(self, peer_id, network_id):
|
||||
body = {"action":"get_random_peer"}
|
||||
self.nosys_core.dispatcher.send_message(body, peer_id, RENDEZVOUS_SERVER_MODULE)
|
||||
|
||||
def on_random_peer(self, event):
|
||||
peer:Peer = event.peer
|
||||
address = event.data["address"]
|
||||
if address:
|
||||
address = address.split(':')
|
||||
self.nosys_core.connections.connect(address=(address[0], int(address[1])), user_id=self.connections[peer.id].connection.security.user, bind_address=peer.connection.bind_address)
|
||||
else:
|
||||
pass
|
||||
|
||||
def on_server_connection(self, event):
|
||||
self.fire_event(RendezvousClientEvents.SERVER_CONNECTED, peer=event.peer)
|
||||
print("CLIENT SERVER CONNECTED")
|
||||
|
||||
def on_server_connection_error(self, event):
|
||||
self.fire_event(RendezvousClientEvents.SERVER_CONNECTION_ERROR, peer=event.peer, error=event.error)
|
||||
print("CLIENT SERVER ERROR")
|
||||
|
||||
def on_server_disconnection(self, event):
|
||||
self.fire_event(RendezvousClientEvents.SERVER_DISCONNECTED, peer=event.peer)
|
||||
print("CLIENT SERVER DISCONNECTED")
|
||||
29
rendezvous/rendezvousClientApiBlueprint.py
Normal file
29
rendezvous/rendezvousClientApiBlueprint.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from libs.fspn.protocol.server import Server
|
||||
from libs.fspn.protocol.connection import Connection, EVENTS as CONNECTION_EVENTS
|
||||
from libs.api.apiBlueprint import ApiBlueprint
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
import logging
|
||||
import random
|
||||
|
||||
class Blueprint(ApiBlueprint):
|
||||
def routes(self):
|
||||
from .rendezvousClient import RendezvousClient
|
||||
self.module:RendezvousClient = self.module
|
||||
|
||||
@self.blueprint.route('/')
|
||||
def show():
|
||||
return self.module.name
|
||||
|
||||
@self.blueprint.route('/connect')
|
||||
def connect_server():
|
||||
self.module.connect_to_server(('127.0.0.1', 30331))
|
||||
return jsonify()
|
||||
|
||||
@self.blueprint.route('/getRandomPeer')
|
||||
def get_random_peer():
|
||||
self.module.send_get_random_peer()
|
||||
return jsonify({"status":"success"})
|
||||
|
||||
|
||||
22
rendezvous/rendezvousClientSocketio.py
Normal file
22
rendezvous/rendezvousClientSocketio.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from libs.noSys.noSysModule import NoSysModule
|
||||
from libs.api.apiBlueprint import ApiBlueprint
|
||||
from libs.api.eventsSocketio import EventsSocketio
|
||||
from libs.app.common.logging import get_logger
|
||||
|
||||
import logging
|
||||
import random
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
class HandlerSocketio(EventsSocketio):
|
||||
def events(self):
|
||||
@self.socketio.on("message", namespace=self.namespace)
|
||||
def on_message(*args, **kwargs):
|
||||
logger.debug('Rendezvous Message',args, kwargs)
|
||||
|
||||
@self.socketio.on("ola", namespace=self.namespace)
|
||||
def on_test(*args, **kwargs):
|
||||
logger.debug('Rendezvous ola',args, kwargs)
|
||||
self.socketio.send(f"Ola from Rendezvous {args[0]}", namespace=self.namespace)
|
||||
|
||||
|
||||
66
rendezvous/rendezvousServer.py
Normal file
66
rendezvous/rendezvousServer.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import logging
|
||||
import random
|
||||
import ipaddress
|
||||
|
||||
from libs.fspn.protocol.server import Server
|
||||
from libs.fspn.protocol.connection import Connection, EVENTS as CONNECTION_EVENTS
|
||||
from libs.noSys.peers import Peer, ConnectionState
|
||||
from libs.noSys.noSysModule import NoSysModule
|
||||
from libs.noSys.noSysModuleServer import NoSysModuleServer
|
||||
from libs.noSys.events import Events as nosys_events, DynamicEvents as nosys_dynamic_events
|
||||
from .rendezvousServerApiBlueprint import Blueprint
|
||||
from libs.app.common.logging import get_logger
|
||||
|
||||
|
||||
logger = get_logger("server")
|
||||
RENDEZVOUS_CLIENT_MODULE = ("rendezvous", "rendezvousClient")
|
||||
|
||||
class RendezvousServer(NoSysModuleServer):
|
||||
def __init__(self, nosys_core):
|
||||
super().__init__(nosys_core)
|
||||
|
||||
def setup(self):
|
||||
super().setup()
|
||||
self.host = self.config.get("server").get("host")
|
||||
self.port = self.config.get("server").get("port")
|
||||
|
||||
self.nosys_core.modules.api.register_blueprint(Blueprint(self).blueprint)
|
||||
|
||||
def on_nosys_ready(self, event):
|
||||
# TODO Subscribe to event new_user in NoSys. If user in auto_start
|
||||
pass
|
||||
|
||||
def on_connection(self, peer):
|
||||
print(f"RENDEZVOUS SERVER PEER CONNECTED {peer.connection.address}")
|
||||
|
||||
def on_disconnection(self, peer):
|
||||
print(f"RENDEZVOUS SERVER PEER DISCONNECTED {peer.connection.address}")
|
||||
|
||||
def on_module_message(self, event):
|
||||
logger.debug(f'RENDEZVOUS SERVER {event.__dict__}')
|
||||
handler_action = getattr(self, 'on_'+event.data['action'])
|
||||
handler_action(event)
|
||||
|
||||
def on_get_random_peer(self, event):
|
||||
self.send_random_peer(event.peer)
|
||||
|
||||
def send_random_peer(self, peer: Peer):
|
||||
excepts = [peer.id]
|
||||
available_peers = [x for x in self.clients.keys() if x not in excepts and self.clients[x].state == ConnectionState.CONNECTED]
|
||||
|
||||
chosen_peer = False
|
||||
if available_peers:
|
||||
rand = random.choice(available_peers)
|
||||
chosen_peer = self.clients[rand]
|
||||
|
||||
if chosen_peer:
|
||||
body = {"action":"random_peer"}
|
||||
body["address"] = f'{chosen_peer.connection.address[0]}:{chosen_peer.connection.address[1]}'
|
||||
self.nosys_core.dispatcher.send_message(body, peer.id, RENDEZVOUS_CLIENT_MODULE)
|
||||
|
||||
body["address"] = f"{peer.connection.address[0]}:{peer.connection.address[1]}"
|
||||
self.nosys_core.dispatcher.send_message(body, chosen_peer.id, RENDEZVOUS_CLIENT_MODULE)
|
||||
else:
|
||||
body = {"action":"random_peer"}
|
||||
body["address"] = None
|
||||
self.nosys_core.dispatcher.send_message(body, peer.id, RENDEZVOUS_CLIENT_MODULE)
|
||||
31
rendezvous/rendezvousServerApiBlueprint.py
Normal file
31
rendezvous/rendezvousServerApiBlueprint.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from libs.fspn.protocol.server import Server
|
||||
from libs.fspn.protocol.connection import Connection, EVENTS as CONNECTION_EVENTS
|
||||
from libs.api.apiBlueprint import ApiBlueprint
|
||||
|
||||
from flask import jsonify, request
|
||||
|
||||
import logging
|
||||
import random
|
||||
|
||||
class Blueprint(ApiBlueprint):
|
||||
def routes(self):
|
||||
from .rendezvousServer import RendezvousServer
|
||||
self.module:RendezvousServer = self.module
|
||||
|
||||
@self.blueprint.route('/')
|
||||
def show():
|
||||
return self.module.module_name
|
||||
|
||||
@self.blueprint.route('/server', methods=["GET", "POST"])
|
||||
def server():
|
||||
if request.method == "GET":
|
||||
state = {"host":self.module.host, "port": self.module.port, "running":self.module.running, "user":self.module.user}
|
||||
return jsonify({"server":state, "connections":list(self.module.connections.keys())})
|
||||
elif request.method == "POST":
|
||||
content = request.json
|
||||
user = content["user"]
|
||||
self.module.run_server(user)
|
||||
return jsonify({"status":"success"})
|
||||
|
||||
|
||||
|
||||
43
rendezvous/vue/api/api.js
Normal file
43
rendezvous/vue/api/api.js
Normal file
@@ -0,0 +1,43 @@
|
||||
const apiUrl = "/api/rendezvous";
|
||||
|
||||
export const rendezvousApi = {
|
||||
|
||||
async runServer(user){
|
||||
const data = {"user": user};
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json',},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
try {
|
||||
const response = await fetch(apiUrl+"/server", requestOptions);
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Error starting server: ${response.status} - ${errorText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error starting server:", error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
async getServerStatus(){
|
||||
try {
|
||||
const response = await fetch(apiUrl+"/server")
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Error fetching users: ${response.status} - ${errorText}`);
|
||||
}
|
||||
const result = await response.json()
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error fetching configs:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
}
|
||||
11
rendezvous/vue/components/ClientTab.vue
Normal file
11
rendezvous/vue/components/ClientTab.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
import { TabPanel } from '@headlessui/vue';
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TabPanel>
|
||||
|
||||
</TabPanel>
|
||||
</template>
|
||||
161
rendezvous/vue/components/ServerTab.vue
Normal file
161
rendezvous/vue/components/ServerTab.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { rendezvousApi } from "../api/api";
|
||||
import { noSysApi } from "@/modules/noSys/api/noSysApi";
|
||||
import InputComboBox from "@/components/inputs/InputComboBox.vue";
|
||||
import Button from "@/components/buttons/Button.vue";
|
||||
import Label from "@/components/labels/Label.vue";
|
||||
import { Tab, TabGroup, TabList, TabPanel} from "@headlessui/vue";
|
||||
import ServerTab from "../components/ServerTab.vue";
|
||||
import ClientTab from "../components/ClientTab.vue";
|
||||
import Card from "@/components/cards/Card.vue";
|
||||
import { GlobeAltIcon, ServerIcon, StopCircleIcon } from "@heroicons/vue/24/solid";
|
||||
import InputText from "@/components/inputs/InputText.vue";
|
||||
import CardContent from "@/components/cards/CardContent.vue";
|
||||
|
||||
const user = ref()
|
||||
const users = ref([])
|
||||
|
||||
const status = ref(null)
|
||||
|
||||
async function getStatus(){
|
||||
status.value = await rendezvousApi.getServerStatus()
|
||||
}
|
||||
|
||||
async function getUsers(){
|
||||
users.value = await noSysApi.getUsers()
|
||||
}
|
||||
|
||||
async function runServer(){
|
||||
const result = rendezvousApi.runServer(user.value.id)
|
||||
getStatus()
|
||||
}
|
||||
|
||||
getUsers()
|
||||
getStatus()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TabPanel>
|
||||
<div class="container mx-auto px-6 py-1 space-y-8">
|
||||
|
||||
<Card class=" border-yellow-400/20 p-8">
|
||||
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center space-x-3">
|
||||
<ServerIcon class="text-yellow-400 h-6 w-6" />
|
||||
<h2 class="text-2xl font-bold text-yellow-400">Server</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!status || !status.server.running" class="text-center py-12">
|
||||
<StopCircleIcon class="text-gray-600 mx-auto mb-4 h-7 w-7" />
|
||||
<p class="text-gray-400 text-lg">No server running</p>
|
||||
<p class="text-gray-500 text-sm">Run a server to help a decentralized network</p>
|
||||
|
||||
<div class="mx-auto px-6 mt-2 space-y-2">
|
||||
<div class="flex flex-row gap-3 items-center">
|
||||
<Label>User</Label>
|
||||
<InputComboBox v-model="user" :items="users" labelKey="id" keyField="id" class=" w-96"></InputComboBox>
|
||||
</div>
|
||||
<Button @click="runServer()">Run Server</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="bg-black border border-yellow-400/20 rounded-lg p-6">
|
||||
<div class="grid lg:grid-cols-5 gap-4 items-center">
|
||||
<div>
|
||||
<Label class="text-gray-400 text-s">Host</Label>
|
||||
<p class="text-white font-mono text-s">{{ status.server.host }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-gray-400 text-s">Port</Label>
|
||||
<p class="text-white font-mono text-s">{{ status.server.port }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-gray-400 text-s">Status</Label>
|
||||
<p v-if="status.server.running" class="text-green-400 font-mono text-s">Running</p>
|
||||
<p v-else class="text-red-400 font-mono text-s">Error</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-gray-400 text-s">Connections</Label>
|
||||
<p class="text-white font-mono text-s">{{ status.connections.length }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-gray-400 text-s">User</Label>
|
||||
<p class="text-white font-mono text-s">{{ status.server.user }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card v-if="status.server.running" class=" border-yellow-400/20 p-8">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center space-x-3">
|
||||
<GlobeAltIcon class="text-yellow-400 h-6 w-6" />
|
||||
<h2 class="text-2xl font-bold text-yellow-400">Active Connections</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div v-for="connection in status.connections" :key="connection" class="bg-black border border-yellow-400/20 rounded-lg p-6">
|
||||
<div class="grid lg:grid-cols-7 gap-4 items-center">
|
||||
<div>
|
||||
<Label class="text-gray-400 text-xs">Connection ID</Label>
|
||||
<p class="text-white font-mono text-sm">{ connection.id }</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label class="text-gray-400 text-xs">Status</Label>
|
||||
<!-- <div class="flex items-center space-x-2"
|
||||
:class="{
|
||||
'text-green-400': peer.status === 'CONNECTED',
|
||||
'text-blue-400': peer.status === 'CONNECTING',
|
||||
'text-yellow-400': peer.status === 'HANDSHAKING',
|
||||
'text-red-400': peer.status === 'DISCONNECTED',
|
||||
'text-gray-300': !['CONNECTED', 'CONNECTING', 'HANDSHAKING', 'DISCONNECTED'].includes(peer.status)
|
||||
}">
|
||||
{peer.status}
|
||||
</div> -->
|
||||
<p class="text-white text-sm">
|
||||
{connection.status}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label class="text-gray-400 text-xs">Networks</Label>
|
||||
<!-- TODO Last Activity -->
|
||||
<p class="text-white text-sm">{ 30 }</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label class="text-gray-400 text-xs">Helped</Label>
|
||||
<!-- TODO Last Activity -->
|
||||
<p class="text-white text-sm">{ 10 }</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label class="text-gray-400 text-xs">Last Activity</Label>
|
||||
<!-- TODO Last Activity -->
|
||||
<p class="text-white text-sm">{ new Date().toLocaleTimeString() }</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 pt-4 border-t border-yellow-400/10">
|
||||
<div class="flex justify-between text-xs text-gray-400">
|
||||
<!-- Connected At -->
|
||||
<span>Connected: {new Date(peer.connectedAt).toLocaleString()}</span>
|
||||
<span>
|
||||
Duration: {Math.floor((Date.now() - new Date(peer.connectedAt).getTime()) / 60000)}{" "}
|
||||
minutes
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
</div>
|
||||
</TabPanel>
|
||||
</template>
|
||||
7
rendezvous/vue/router.js
Normal file
7
rendezvous/vue/router.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import HomeView from "./views/HomeView.vue";
|
||||
|
||||
const routes = [
|
||||
{path: '/', name:'rendezvous', component: HomeView},
|
||||
]
|
||||
|
||||
export {routes};
|
||||
46
rendezvous/vue/views/HomeView.vue
Normal file
46
rendezvous/vue/views/HomeView.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<script setup>
|
||||
import { Tab, TabGroup, TabList, TabPanels } from "@headlessui/vue";
|
||||
import ServerTab from "../components/ServerTab.vue";
|
||||
import ClientTab from "../components/ClientTab.vue";
|
||||
import { LinkIcon, ServerIcon } from "@heroicons/vue/24/solid";
|
||||
|
||||
const tabItems = [
|
||||
{label:"Client", icon:LinkIcon, tabComponent:ClientTab},
|
||||
{label:"Server", icon:ServerIcon, tabComponent:ServerTab},
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen bg-black text-yellow-400">
|
||||
<header class="border-b border-yellow-400/20 py-6">
|
||||
<div class="container mx-auto px-6">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="text-3xl font-black">
|
||||
<span class="text-yellow-400">RENDEZ</span>
|
||||
<span class="text-white">VOUS</span>
|
||||
</div>
|
||||
<div class="w-1 h-8 bg-yellow-400"></div>
|
||||
<span class="text-gray-400">Helps Peer-to-Peer Connections</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="container mx-auto px-6">
|
||||
<TabGroup>
|
||||
<TabList class="flex flex-wrap gap-2 border-b border-yellow-400/20">
|
||||
<Tab v-for="tab in tabItems" as="template" :key="tab" v-slot="{ selected }">
|
||||
<button class="flex items-center space-x-2 px-6 py-3 font-semibold transition-all duration-300 border-b-2"
|
||||
:class="{ 'text-yellow-400 border-yellow-400': selected, 'text-gray-400 border-transparent hover:text-yellow-400 hover:border-yellow-400/50': !selected }">
|
||||
<component :is="tab.icon" class="h-4 w-4"></component>
|
||||
<span>{{ tab.label }}</span>
|
||||
</button>
|
||||
</Tab>
|
||||
</TabList>
|
||||
<TabPanels class="mt-6 space-y-6" v-for="tab in tabItems" :key="tab.label">
|
||||
<component :is="tab.tabComponent"></component>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user