Added libs

This commit is contained in:
Lucas
2026-01-25 13:55:46 +10:00
parent 575c682afc
commit f70af3c4ea
229 changed files with 26983 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
const p2privateApiUrl = "/api/p2private"
export const p2privateApi = {
async getNetworks(){
try {
const response = await fetch(p2privateApiUrl+'/networks')
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error fetching networks: ${response.status} - ${errorText}`);
}
const result = await response.json()
// p2postStore.users = result
return result;
} catch (error) {
console.error("Error fetching networks:", error);
throw error;
}
},
async getFriends(){
try {
const response = await fetch(p2privateApiUrl+'/friends')
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error fetching networks: ${response.status} - ${errorText}`);
}
const result = await response.json()
// p2postStore.users = result
return result;
} catch (error) {
console.error("Error fetching networks:", error);
throw error;
}
},
async createMessage(from, to, content, medias){
const data = {"from":from, "to":to, "content":content, "medias":medias}
const requestOptions = {
method: 'POST',
headers: {'Content-Type': 'application/json',},
body: JSON.stringify(data),
};
try {
const response = await fetch(p2privateApiUrl+"/messages", requestOptions);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error fetching posts: ${response.status} - ${errorText}`);
}
const result = await response.json();
return result;
} catch (error) {
console.error("Error fetching posts:", error);
throw error;
}
},
// async createPosts(user, content, medias, networks){
// const data = {"user": user, "content": content, "medias": medias, "networks": networks};
// const requestOptions = {
// method: 'POST',
// headers: {'Content-Type': 'application/json',},
// body: JSON.stringify(data),
// };
// try {
// const response = await fetch(p2postApiUrl+"/posts", requestOptions);
// if (!response.ok) {
// const errorText = await response.text();
// throw new Error(`Error fetching posts: ${response.status} - ${errorText}`);
// }
// const result = await response.json();
// return result;
// } catch (error) {
// console.error("Error fetching posts:", error);
// throw error;
// }
// },
// async uploadFile(file) {
// const formData = new FormData();
// formData.append("file", file);
// try {
// const response = await fetch(p2postApiUrl + "/medias", {
// method: "POST",
// body: formData,
// });
// if (!response.ok) {
// const errorText = await response.text();
// throw new Error(`Error uploading file: ${response.status} - ${errorText}`);
// }
// const result = await response.json();
// return result;
// } catch (error) {
// console.error("Error uploading file:", error);
// throw error;
// }
// }
}

View File

@@ -0,0 +1,12 @@
export default function registerSocketEvents(socket) {
socket.on('connect', (data) => {
console.log('Connected p2Private')
})
// socket.on('newPost', (data) => {
// p2postStore.addPost(data)
// console.log('New Post', data)
// })
}

7
p2private/vue/router.js Normal file
View File

@@ -0,0 +1,7 @@
import HomeView from "./views/HomeView.vue";
const routes = [
{path: '/', name:'p2private', component: HomeView},
]
export {routes};

View File

@@ -0,0 +1,254 @@
<script setup>
import Button from '@/components/buttons/Button.vue';
import Card from '@/components/cards/Card.vue';
import InputComboBox from '@/components/inputs/InputComboBox.vue';
import InputText from '@/components/inputs/InputText.vue';
import Label from '@/components/labels/Label.vue';
import { CheckIcon, DocumentTextIcon, EllipsisHorizontalIcon, GlobeAltIcon, LockClosedIcon, PaperAirplaneIcon, PaperClipIcon, ShieldCheckIcon, ShieldExclamationIcon, UserPlusIcon, UsersIcon, WifiIcon } from '@heroicons/vue/24/solid';
import { onMounted, ref } from 'vue';
import { p2privateApi } from '../api/p2privateApi';
import { p2postApi } from '@/modules/p2post/api/p2postApi';
// const friends = ref([, {nickname:'Nickname2', unreadCount:2, publicKey:'1234aed233423c', isOnline:false, lastSeen:new Date()}])
const friends = ref([])
const activeFriend = ref(null)
const myUsers = ref([])
const relays = ref([])
async function getRelays(){
relays.value = await p2privateApi.getNetworks()
}
async function getFriends(){
const result = await p2privateApi.getFriends()
for (const f of result){
const m1 = {id:1, senderId:'me', content:"test Meeeeeeeeeeeeeeeeeeeeee", timestamp:new Date(), status:"SENT"}
const m2 = {id:2, senderId:'peer', content:"test Peer", status:"RECEIVED", timestamp:new Date(), files:[{name:'file1', type:'image/jpeg', size:123}]}
const mes = [m1,m2]
const fr = {nickname:'Nickname', unreadCount:5, publicKey:f.pubkey, isOnline:true, messages:mes}
friends.value.push(fr)
}
}
async function getMyUsers(){
myUsers.value = await p2postApi.getMyUsers()
}
const inputMessageText = ref("")
async function createMessage(){
const randomIndex = Math.floor(Math.random() * myUsers.value.length);
const randomUser = myUsers.value[randomIndex]
await p2privateApi.createMessage(randomUser.pubkey, activeFriend.value.publicKey, inputMessageText.value, [])
}
onMounted(()=>{
getRelays()
getFriends()
getMyUsers()
})
</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">P2</span>
<span class="text-white">PRIVATE</span>
</div>
<div class="w-1 h-8 bg-yellow-400"></div>
<span class="text-gray-400">Encrypted Peer-to-Peer Messaging</span>
</div>
</div>
</header>
<div class="flex h-screen">
<!-- Sidebar -->
<div class="w-80 border-r border-yellow-400/20 flex flex-col">
<!-- Add Peer -->
<Card class="m-4 border-yellow-400/20 p-4">
<h3 class="text-lg font-bold text-yellow-400 mb-4 flex items-center">
<UserPlusIcon class="h-5 w-5 mr-2" />
Add Friend
</h3>
<div class="space-y-3">
<div>
<Label class="text-gray-300 mb-2 block text-sm">Public Key</Label>
<InputText class="bg-black border-yellow-400/30 text-white font-mono text-sm" placeholder="Public Key"/>
</div>
<div>
<Label class="text-gray-300 mb-2 block text-sm">Relay Networks</Label>
<div class="space-y-2">
<div v-for="relay of relays" class="flex items-center space-x-2">
<input type="checkbox" :id="relay.id" name="options" :value="relay.name">
<Label class="text-white text-sm">
{{relay.name}}
</Label>
</div>
</div>
</div>
<Button @click="addFriend" class="w-full bg-yellow-400 hover:bg-yellow-300">
<UserPlusIcon class="h-5 w-5 mr-2 text-black" />
<span class="text-black">Add Peer</span>
</Button>
</div>
</Card>
<!-- Friends list -->
<div class="flex-1 overflow-hidden flex flex-col">
<div class="px-4 py-2 border-b border-yellow-400/20">
<h3 class="text-lg font-bold text-yellow-400 flex items-center">
<UsersIcon class="w-5 h-5 mr-2" />
Friends ({{friends.length}})
</h3>
</div>
<div class="flex-1 overflow-y-auto p-4 space-y-2">
<div v-for="friend in friends">
<div class="flex items-center space-x-3" @click="activeFriend = friend" :class="friend === activeFriend ? 'border rounded-lg border-yellow-400/30 p-1' : 'p-3'">
<div class="relative">
<img src="" alt="" class="w-12 h-12 rounded-full" />
<div class="absolute -bottom-1 -right-1 w-4 h-4 rounded-full border-2 border-gray-500"
:class="{'bg-green-400' : true}"
></div>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between">
<span class="text-white font-semibold truncate">{{friend.nickname}}</span>
<span v-if="friend.unreadCount>0" class="bg-yellow-400 text-black text-xs font-bold px-2 py-1 rounded-full">
{{friend.unreadCount}}
</span>
</div>
<p class="text-xs text-gray-400 font-mono truncate">{{friend.publicKey}}</p>
<div class="flex items-center space-x-1 mt-1 text-xs text-gray-500">
<div v-if="friend.isOnline" class="flex flex-row">
<WifiIcon class="w-5 h-5 text-green-400" />
Online
</div>
<div v-if="!friend.isOnline" class="flex flex-row">
<WifiIcon class="h-5 w-5 text-gray-400" />
<!-- Last seen {{ friend.lastSeen.toLocaleTimeString() }} -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Chat -->
<div v-if="activeFriend" class="flex-1 flex flex-col">
<!-- Chat Header -->
<div class="border-b border-yellow-400/20 p-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3">
<div class="relative">
<img
src=""
alt=""
class="w-10 h-10 rounded-full"
/>
<div class="absolute -bottom-1 -right-1 w-3 h-3 rounded-full border-2 border-gray-500"
:class="{'bg-green-400':activeFriend.isOnline}">
</div>
</div>
<div>
<h3 class="text-white font-semibold">{{activeFriend.nickname}}</h3>
<p class="text-xs text-gray-400 font-mono">{{activeFriend.publicKey}}</p>
</div>
</div>
<div class="flex items-center space-x-2">
<div class="flex items-center space-x-1 text-green-400">
<LockClosedIcon class="h-5 w-5" />
<span class="text-sm">End-to-End Encrypted</span>
</div>
<div class="flex items-center space-x-1 text-yellow-400">
<GlobeAltIcon class="h-5 w-5"/>
<span class="text-sm">Via Network Relay</span>
</div>
</div>
</div>
</div>
<!-- Messages -->
<div class="flex-1 overflow-y-auto p-4 space-y-4">
<div v-for="message in activeFriend.messages"
:key="message.id"
class="flex justify-start"
:class="{'justify-end':message.senderId=='me'}"
>
<!-- Message Content -->
<div class="max-w-xs lg:max-w-md px-4 py-2 rounded-lg" :class="message.senderId == 'me' ? 'bg-yellow-400 text-black' : 'bg-gray-900 text-white'">
{{message.content}}
<!-- Files -->
<div class="space-y-2 mb-2">
<div v-for="file in message.files" class="flex items-center space-x-2 p-2 rounded">
<Button class="p-1 h-auto hover:bg-black/20 gap-2">
<DocumentTextIcon class="w-5 h-5" />
<p class=" text-s">{{file.name}}</p>
<p class="text-xs opacity-70">{{file.size}}</p>
</Button>
</div>
</div>
<!-- Message Footer -->
<div class="flex items-center justify-between text-xs opacity-80">
<span>{{new Date(message.timestamp).toLocaleTimeString()}}</span>
<div class="flex items-center space-x-1">
<ShieldCheckIcon v-if="true" class="w-4 h-4"/>
<ShieldExclamationIcon v-else class="w-4 h-4"/>
<CheckIcon v-if="true" class="w-4 h-4"/>
<EllipsisHorizontalIcon v-else class="w-4 h-4"/>
</div>
</div>
</div>
</div>
</div>
<!-- Message Input -->
<div class="sticky bottom-0 border-t border-yellow-400/20 p-4">
<!-- File Preview TODO from create post preview-->
<div v-if="false" class="mb-3 p-3 bg-black border border-yellow-400/20 rounded-lg">
</div>
<div class="flex items-end space-x-2">
<Button class="border border-yellow-400 text-yellow-400 hover:bg-yellow-400 hover:text-black bg-transparent">
Add Files
</Button>
<div class="flex-1">
<InputText class="bg-black border-yellow-400/30 text-white resize-none" v-model="inputMessageText"/>
</div>
<Button @click="createMessage()" class="bg-yellow-400 text-black hover:bg-yellow-300">
<!-- <Send size={16} /> -->
Send
</Button>
</div>
</div>
</div>
<!-- No Chat Selected -->
<div v-if="!activeFriend" class="flex-1 flex items-center justify-center">
<div class="text-center">
<LockClosedIcon class="w-10 h-10 text-gray-600 mx-auto mb-4" />
<h3 class="text-xl font-semibold text-gray-400 mb-2">Select a friend to start chatting</h3>
<p class="text-gray-500">Your messages are end-to-end encrypted and decentralized</p>
</div>
</div>
</div>
</div>
</template>