diff --git a/README.md b/README.md index 302642c..5b50f66 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,295 @@ Bağlantı TCP tabanlı yüksek hızlı WebSocket protokolüne dayanır ve sunuc ## Proje tarafından uygulanan load balance teknolojisi -![image](https://git.saqut.com/saqut/storage/raw/branch/master/Diagram1.png) \ No newline at end of file +![image](https://git.saqut.com/saqut/storage/raw/branch/master/Diagram1.png) + +# Geliştirici Dökümantasyonu + +## Kurulum + +### Proje ortamına kurulumu + +```html + +``` + +### Geliştirme ortamına kurulumu + +```javascript +const mwse = new MWSE({ + endpoint: "wss://ws.saqut.com/" // MSWS kurulu sunucu adresi +}); +mwse.scope(async () => { + // Bağlantı sağlandığında burası tetiklenir +}) +``` + +### Kendi bağlantı kimliğini öğrenme + +```javascript +mwse.scope(async () => { + let me = mwse.peer('me'); // Kendi bağlantınız üzerinde işlem yaparken `me` olarak bahsedersiniz + console.log(me.socketId); // Her eşin tekil bir socketIdsi vardır +}) +``` + + +### Sanal Adres ayırma / yeniden ayırma / kaldırma +```javascript +mwse.scope(async () => { + let me = mwse.peer('me'); + + /** + * Sanal adresler size veri gönderilmek istendiğinde veya etkileşime + * geçilmesi istendiğinde ona socketId gibi bir UUID yerine sizi temsil eden daha kısa + * ip adresi, sayı veya kısa bir kod ile aynı şeyleri yapmanıza olanak tanır. + * Aynı anda hem sanal ip adres, sayı ve kısa koduna sahip olabilirsiniz + * ancak aynı türden temsil koduna (mesela kısa koddan) birden fazla sahip olamazsınız + * Yeni bir bağlantı daha açmanız gerekir + **/ + + // Bağlantınıze özel sanal tekil ip adresi kaynağı ayırın + let ipadress = await me.virtualPressure.allocAPIPAddress(); + + // Bağlantınıze özel sanal tekil numara kaynağı ayırın + let numberaddress = await me.virtualPressure.allocAPNumber(); + + // Bağlantınıze özel sanal kod kaynağı ayırın + let shortcodeadress = await me.virtualPressure.allocAPShortCode(); + + // Bütün bu kaynakları yenileriyle değiştirmek için + // her birinin ayrı ayrı yeniden alma işlevleri vardır + // Bir adresi yenilediğinizde artık eski adres kullanılmaz olur + me.virtualPressure.reallocAPIPAddress(); + me.virtualPressure.reallocAPNumber(); + me.virtualPressure.reallocAPShortCode(); + + // Bütün bu kaynakları kaldırmak için her birinin ayrı ayrı + // bırakma işlevi vardır + // Bir adresi kullanmadığınızda artık bu adreslerden size + // ulaşılamaz olursunuz + await me.virtualPressure.releaseAPIPAddress(); + await me.virtualPressure.releaseAPNumber(); + await me.virtualPressure.releaseAPShortCode(); + + await me.virtualPressure.queryAPIPAddress(); + await me.virtualPressure.queryAPNumber(); + await me.virtualPressure.queryAPShortCode(); +}) +``` + +### Farklı bir eşe erişme + +```javascript +mwse.scope(async () => { + let peer = mwse.peer('325a8f7f-eaaf-4c21-855e-9e965c0d5ac9') // Diğer eşin socketId'sini belirtiyoruz + let me = mwse.peer('me'); + + // Eşin ulaşılabilir (online) olup olmadığını verir + let isOnline = await peer.isReachable(); + + if(isOnline) + { + // İleitşim kurmak için istek gönderiyoruz + await peer.requestPair(); + } + + // Bize gönderilecek olan istekleri dinliyoruz + me.on('request/pair', peer => { + // İstek gönderen kullanıcının bilgileri peer içinde bulunur + // iletişime devam etmek için isteği kabul ediyoruz + peer.acceptPair(); + // veya istemiyorsak reddediyoruz + peer.rejectPair(); + }) + + // Karşımızdaki kullanıcının ne cevap verdiğini + // anlamak için sistem ayrı ayrı cevaplar üretir + + // Kabul ettiyse accept eventini tetikler + me.on('accepted/pair', peer => { + // İstek gönderen kullanıcının bilgileri peer içinde bulunur + }) + + // Reddettiyse veya iletişimi sonlandırmışsa + // end eventi tetiklenir + me.on('end/pair', peer => { + // Kullanıcının bilgileri peer içinde bulunur + }) +}) +``` + +Eğer uygulamanızda bu şekilde bir erişim metodolojisi kullanmak istemiyorsanız +pair sistemini elle kapatabilirsiniz + +```javascript +mwse.scope(async () => { + let me = mwse.peer('me'); + + // Tüm kullanıcılar size mesaj iletebilir + await peer.disablePairAuth(); + + // Sadece eşleşmiş kullanıcılar size mesaj iletebilir + await peer.enablePairAuth(); +}) +``` +## Tünelleme ile mesajlaşma + +MWSE üzerinde karşılıklı mesajlaşma için + metodoloji bulunur bunlardan birisi serbest mesajlaşmadır ve + bu şekilde çalışır +```javascript +mwse.scope(async () => { + let me = mwse.peer('325a8f7f-eaaf-4c21-855e-9e965c0d5ac9'); + + // Tüm kullanıcılar size mesaj iletebilir + await peer.disablePairAuth(); + + // Bu şekilde serbest bir şekilde herhangi bir zamanda karşılıksız mesaj iletebilirsiniz + peer.send({ + text: "Good morning" + }) + + // Kullanıcı her hangi bir mesaj gönderdiğinde burası tetiklenir + peer.on('message', message => { + if(text.message == "Good morning") + { + // Eğer mesaj için bir cevap bekliyorsa cevap veriyoruz + peer.send("You are welcome"); + }else{ + // Ancak mesaj önceden gönderdiğimiz isteğin cevabıysa görüntülüyoruz + console.log("Reply is :", message) // --> You are welcome + } + }) +}) +``` +2. yöntem ise el sıkışmalı mesajlaşmadır ve gönderdiğiniz bir mesaja verilen karşılık olarak +sistem, gelen mesajın gönderdiğiniz hangi mesaja karşılık olarak gönderildiğini tuttuğu için +request/response şeklinde ilerleyebilir + +```javascript +mwse.scope(async () => { + let peer = mwse.peer('325a8f7f-eaaf-4c21-855e-9e965c0d5ac9'); + let me = mwse.peer('me'); + + // Tüm kullanıcılar size mesaj iletebilir + await me.disablePairAuth(); + + // Bizden istenecek veriler için önceden cevapları hazırlıyoruz + me.on('request', ({body, response}) => { + switch(body.message) + { + case "Good morning":{ + // do anything... + response("You are welcome") + break; + } + } + }) + + + // Bu şekilde serbest bir şekilde herhangi bir zamanda karşılıksız mesaj iletebilirsiniz + let response = await peer.request({ + message: "Good morning" + }); + + console.log(response) // response ---> You are welcome +}) +``` + +Bu şekilde hem okunabilirlik artar hemde mesajlar karşılıklı olarak etki-tepki şeklinde ilerler + +---- + + +### Oda kurma ve kapatma + +Oda sistemi kullanıcıların bir araya gelerek, tek seferde bir grubun +toplu olarak birbirleriyle mesajlaşabileceği bir yapıdır. +Kişiler birbirlerini bulabilir, toplu mesajlar iletebilirler, odaya +katılabilir veya ayrılabilirler + + +Odanın ayarlarını, odayı ilk oluşturan kişi belirler ve oda bir kez oluşturulduğunda yeniden aynı isimle oda açılamaz +Oda türü (joinType) herkese açık `free`, davet ile `invite`, şifreli `password` veya herkese kapalı `lock` olabilir. + +```javascript + +mwse.scope(async () => { + + let room = mwse.room({ + name: "Oda ismi", // odanın görünür ismi + description: "Oda açıklaması", // odanın görünecek ismi + joinType: "free", // herkese açık oda + credential: "****", // varsa şifre + notifyActionInvite: false, // yeni biri katılmak istediğinde tüm peerlere haber iletme özelliği + notifyActionJoined: true, // yeni biri katıldığında tüm peerlere haber iletir + notifyActionEjected: true, // Biri odadan ayrıldığında tüm peerlere haber iletir + ifexistsJoin: false // true ayarlanırsa, oluşturma aşamasında oda zaten var yanıtı alırsa hata vermek yerine odaya katılır + }); + + // Verilen isime ait odaya katılmak için bu komut yeterlidir + await room.join(); // --> odaya katılırken sorun oluşursa hata fırlatır + + // Verilen ayarlara sahip odayı oluşturmak için aşağıdaki komut yeterlidir + await room.createRoom(); // -> oda oluştururken sorun oluşursa hata fırlatır + + // Var olan bir odadan ayrılmak için bu komut kullanılabilir + await room.eject(); + + // İçinde bulunduğu odanın tüm eşlerinin idsini verir + let peers = await room.fetchPeers(); +}); +``` + +### Oda içerisinde iletişim + +Odaya katıldıktan sonra katılan kişiler `room` üzerinden mesaj iletebilirsiniz, verilen her mesajı sistem istisnasız tüm oda katılımcılarına iletmekten sorumludur +Oda içerisindeki kişiler ile oda içindeki kişilerin listesini alarak özel iletişimede geçebilirsiniz ancak aynı odadaki kişiler sistem tarafında yinede güvenilir kabul edilmez +Sistem pairAuth güvenliği kapalı olan kişilere mesajlar iletir ancak açık olan kullanıcıların her biri için ayrı ayrı eşleşme isteği göndermelisiniz + +```javascript + +mwse.scope(async () => { + + let room = mwse.room({ + name: "Oda ismi", // odanın görünür ismi + description: "Oda açıklaması", // odanın görünecek ismi + joinType: "free", // herkese açık oda + notifyActionJoined: true, // yeni biri katıldığında tüm peerlere haber iletir + ifexistsJoin: false // true ayarlanırsa, oluşturma aşamasında oda zaten var yanıtı alırsa hata vermek yerine odaya katılır + }); + + // Verilen ayarlara sahip odayı oluşturmak için aşağıdaki komut yeterlidir + await room.createRoom(); // -> oda oluştururken sorun oluşursa hata fırlatır + + // Oda içerisinde mesaj gönderildiğinde gönderilen paketi dinlemek için aşağıdaki komut kullanılabilir + room.on('message', message => { + // Odaya `message` paketi iletildi + }) + + // Odaya yeni birisi katıldığında haber almak için aşağıdaki komut kullanılabilir + room.on('join', peer => { + // Odaya katılan kişinin bilgisi peerde tutulur + // Odaya katılan kişiyle bağlantı kurup, oda dışında kendisiyle iletişimede geçebilirsiniz + peer.requestPair(); + }) + + // Odaya birisi ayrıldığında haber almak için aşağıdaki komut kullanılabilir + room.on('eject', peer => { + // Odadan ayrılan kişinin bilgisi peerde tutulur + }) + + // Odadaki herkese mesaj iletmek için room içerisindeki send komutu kullanılabilir + room.send({ + message: "Good year !" + }); + + // Oda kapatıldığında close tetiklenir + room.on('close', () => { }) +}); +``` + +### Odayı kapatma + +Odalar odaya ait olan bağlantılar üzerine kurulur, tüm bağlantılar odadan çıktığında oda otomatik olarak kapatılır \ No newline at end of file diff --git a/Source/Client.js b/Source/Client.js index 9824ba9..98e2835 100644 --- a/Source/Client.js +++ b/Source/Client.js @@ -27,6 +27,9 @@ function Client() * @type {Set} */ this.rooms = new Set(); + /** + * @type {Set} + */ this.pairs = new Set(); this.requiredPair = false; @@ -151,7 +154,7 @@ Client.prototype.rejectPeerRequest = function(client){ this.sync('pairs'); client.send([{ from: this.id - },'rejected/pair']); + },'end/pair']); }; /** * @param {Client|string} client @@ -164,6 +167,9 @@ Client.prototype.isPaired = function(client){ } return client.pairs.has(this.id) && this.pairs.has(client.id); }; +/** + * @returns {string[]} + */ Client.prototype.pairList = function(){ return [...this.pairs.values()].filter(e => this.isPaired(e)); }; diff --git a/Source/Services/Auth.js b/Source/Services/Auth.js index abb6567..1ce2895 100644 --- a/Source/Services/Auth.js +++ b/Source/Services/Auth.js @@ -127,10 +127,28 @@ addService(({ client.rejectPeerRequest(pairclient); break; } + case 'end/pair':{ + if(Client.clients.has(to)){ + return end({ + status: 'fail', + message: 'CLIENT-NOT-FOUND' + }) + }; + let pairclient = Client.clients.get(to); + if(!pairclient.pairs.has(client.id)) + { + return end({ + status: 'success', + message: 'NOT-PAIRED' + }) + } + client.rejectPeerRequest(pairclient); + break; + } case 'pair/list':{ end({ type:'pair/list', - value: pairList + value: client.pairList() }) break; } @@ -149,38 +167,6 @@ addService(({ } break; } - // case 'auth/check':{ - // let auth = client.store.has('user'); - // return end({ - // value: auth - // }) - // } - // case 'auth/login':{ - // if(username == '*' && password == '*') - // { - // return end({ - // status: 'success' - // }) - // }else{ - // return end({ - // status: 'fail' - // }) - // } - // } - // case 'auth/logout':{ - // let auth = client.store.has('user'); - // if(auth) - // { - // client.store.delete('user'); - // return end({ - // status: 'success' - // }) - // }else{ - // return end({ - // status: 'fail' - // }) - // } - // } case 'auth/info':{ client.info.set(name, value); let clients = client.getSucureClients(); diff --git a/frontend/Peer.ts b/frontend/Peer.ts index 92c3df7..422117e 100644 --- a/frontend/Peer.ts +++ b/frontend/Peer.ts @@ -119,6 +119,68 @@ export default class Peer extends EventTarget value: 'disable' }); } + async requestPair() + { + let {message,status} = await this.mwse.EventPooling.request({ + type:'request/pair', + to: this.socketId + }); + if( + message == "ALREADY-PAIRED" || + message == "ALREADY-REQUESTED" + ) + { + console.warn("Already paired or pair requested") + }; + if(status == "fail") + { + console.error("Request Pair Error",status, message); + return false; + } + return true; + } + async endPair() + { + await this.mwse.EventPooling.request({ + type:'end/pair', + to: this.socketId + }); + this.forget(); + } + async acceptPair() + { + let {message,status} = await this.mwse.EventPooling.request({ + type:'accept/pair', + to: this.socketId + }); + if(status == "fail") + { + console.error("Pair Error",status, message); + return false; + } + return true; + } + async rejectPair() + { + let {message,status} = await this.mwse.EventPooling.request({ + type:'reject/pair', + to: this.socketId + }); + if(status == "fail") + { + console.error("Pair Error",status, message); + return false; + } + return true; + } + async getPairedList() : Promise + { + let {value} = await this.mwse.EventPooling.request({ + type:'pair/list', + to: this.socketId + }); + return value; + } async send(pack: any){ let isOpenedP2P = this.peerConnection; let isOpenedServer = this.mwse.server.connected; diff --git a/frontend/index.ts b/frontend/index.ts index 2b1feee..cd08b17 100644 --- a/frontend/index.ts +++ b/frontend/index.ts @@ -99,13 +99,30 @@ export default class MWSE extends EventTarget { }) this.EventPooling.signal("pair/info", (payload : {from : string,name: string, value: string | number | boolean}) => { let {from, name, value} = payload; - let peer = this.peer(from); - peer.info.info[name] = value; - peer.emit("info", name, value); }) + this.EventPooling.signal("request/pair", (payload : {from : string,info: any}) => { + let {from, info} = payload; + let peer = this.peer(from); + peer.info.info = info; + peer.emit("request/pair", peer); + this.peer('me').emit('request/pair', peer); + }) + this.EventPooling.signal("accepted/pair", (payload : {from : string,info: any}) => { + let {from, info} = payload; + let peer = this.peer(from); + peer.info.info = info; + peer.emit("accepted/pair", peer); + this.peer('me').emit('accepted/pairr', peer); + }) + this.EventPooling.signal("end/pair", (payload : {from : string,info: any}) => { + let {from, info} = payload; + let peer = this.peer(from); + peer.emit("endPair", info); + this.peer('me').emit('endPair', from, info); + }) } public room(options: IRoomOptions | string) : Room {