Compare commits
No commits in common. "stable" and "perfectnogation" have entirely different histories.
stable
...
perfectnog
60
README.md
60
README.md
|
|
@ -1,59 +1,23 @@
|
|||
# MWSE — Micro WebSocket Engine
|
||||
# MWSE Nedir?
|
||||
|
||||
> **Durum: Go yeniden yazımı (0.1.0) başlıyor.** Mevcut Node.js engine `ws.saqut.com`'da canlı; çekirdek, concurrency'i doğru çözmek için Go'ya taşınıyor. Frontend SDK'nın giriş/çıkış sözleşmesi **değişmiyor**.
|
||||
MWSE yani Micro Web Socket Engine, kendisine bağlanan eşleri birbirleriyle ile eşleştirerek, eşler arası veri tünelleri oluşturan geniş ölçekli bir mikroservistir.
|
||||
|
||||
MWSE, kendisine bağlanan eşleri (peer) birbirleriyle eşleştirip aralarında **düşük gecikmeli veri tünelleri** kuran bir gerçek-zamanlı iletişim motorudur. Sunucu cihazları sanallaştırır; eşler birbirlerinin IP'sini veya cihaz türünü bilmeden, çift yönlü serbestçe haberleşir. Üstünde oda yönetimi, peer pairing, sanal adresleme, dosya transferi ve WebRTC sinyalleşmesi gelir.
|
||||
Servis, bağlantı sağlayan cihazların verilerini kendi aralarında senkron etmek için kullanılabilir, cihazları gruplayabilir, odalar oluşturabilir, sohbet ve görüntülü görüşme yazılımları için alt yapı olarak kullanılabilir
|
||||
|
||||
## Neden var?
|
||||
Bağlantı TCP tabanlı yüksek hızlı WebSocket protokolüne dayanır ve sunucunun cihazları sanallaştırması sayesinde diğer kişilerin IP adreslerini veya cihaz türü gibi bilgilere ihtiyaç duymadan düşük gecikmeli çift taraflı serbest iletişim kurmalarını sağlar.
|
||||
|
||||
Gerçek-zamanlı uygulama altyapısı bugün ya sadece transport (Socket.io) ya da pahalı SaaS (Twilio/LiveKit). MWSE'nin hedefi: **birkaç satırda** oda + mesaj + sesli/görüntülü görüşme kurabileceğin, kendi sunucunda çalışan açık bir motor.
|
||||
[Geliştirici Dökümanı](https://git.saqut.com/saqut/MWSE/wiki/Entegrasyon)
|
||||
|
||||
```js
|
||||
const mwse = new MWSE({ endpoint: "wss://ws.saqut.com/" });
|
||||
# Güvenlik !
|
||||
|
||||
mwse.scope(async () => {
|
||||
const room = mwse.room({ name: "lobi", joinType: "free", ifexistsJoin: true });
|
||||
await room.join();
|
||||
room.on("message", (peer, data) => console.log(peer.socketId, data));
|
||||
room.broadcast("herkese merhaba");
|
||||
});
|
||||
```
|
||||
Framework, bağlı tüm cihazlar arasında mesajları doğru hedefe, verinin bozulmadığını garanti ederek iletmekden sorumludur.
|
||||
|
||||
- **~20 satır** → grup mesajlaşma
|
||||
- **~100 satır** → sesli görüşme
|
||||
- **~500 satır** → görüntülü görüşme
|
||||
Bunların dışında hassas verilerin soket üzerinden iletilmesi şimdilik önerilmez, clientlerin ileteceği mesajlar **SOKETE İLETİLMEDEN ÖNCE** kullanıcılar tarafından manipüle edilebilir veya taklit edilebilir ve MWSE bunun doğrulamasını **YAPMAZ**
|
||||
|
||||
Tam örnekler için → [Wiki](https://git.saqut.com/saqut/MWSE/wiki/Home).
|
||||
## WebSocket topolojisi
|
||||
|
||||
## Mimari (özet)
|
||||

|
||||
|
||||
```
|
||||
[Client SDK] ⇄ WSTS protokolü ⇄ [MWSE Engine]
|
||||
(frontend/) (paket kimliklendirme, (Source/: WebSocket, MessageRouter,
|
||||
request/response/stream) Services/{Auth, Room, Session,
|
||||
IPPressure, DataTransfer, YourID})
|
||||
```
|
||||
## Proje tarafından uygulanan load balance teknolojisi
|
||||
|
||||
- **WSTS protokolü:** WebSocket üzerine kurulu, giden/gelen paketleri kimliklendiren ve "kimden/kime" yönlendiren request-response-stream katmanı.
|
||||
- **Sanal adresleme (IPPressure):** uzun socket hash'leri yerine sanal IP / numara / kısa kod (ör. `15.214.11.74`, `884`, `ZQT`) atayıp eşleri kolayca çağırma.
|
||||
- **Oda sistemi:** `free` / `invite` / `password` / `lock` katılım türleri, per-connection okuma/yazma/bildirim ayarları.
|
||||
|
||||
## Yol haritası
|
||||
|
||||
| Sürüm | İçerik |
|
||||
|------|--------|
|
||||
| **0.1.0** | Go engine çekirdeği + concurrency (goroutine/channel/mutex) + `go test -race` süreç testleri |
|
||||
| **1.0.0** | Go + Frontend + WebRTC tam parite (oda, pairing, sanal adres, tünel, signaling, demolar) |
|
||||
| **2.0.0** | WebRTC Studio: tam WebRTC API, çoklu track/kamera, canvas compositing, SRS yayını, sanal IP çakışma + alt-network |
|
||||
| **2.5.0** | JSON → binary protokol |
|
||||
| **3.0.0** | Platform: Notify (offline/suit), aktif/pasif sync + datastore, 3. parti sunucu köprüsü |
|
||||
|
||||
Detaylı görevler **Issues** ve milestone'larda. Tasarım bağlamı: `todo.md`.
|
||||
|
||||
## Güvenlik
|
||||
|
||||
MWSE mesajları doğru hedefe ve bozulmadan iletmekten sorumludur. Ancak istemcilerin gönderdiği mesajlar **sokete iletilmeden önce** kullanıcı tarafında manipüle/taklit edilebilir; MWSE bunu **doğrulamaz**. Hassas verileri uygulama katmanında doğrula/şifrele.
|
||||
|
||||
## Lisans
|
||||
|
||||
Bkz. `LICENSE`.
|
||||

|
||||
|
|
@ -1,3 +1,5 @@
|
|||
const { CLIENT_SEND_MESSAGE, CLIENT_UPDATE_PROP } = require("./IPC");
|
||||
const stats = require("./stats");
|
||||
function Client()
|
||||
{
|
||||
/**
|
||||
|
|
@ -34,6 +36,16 @@ function Client()
|
|||
this.APNumber = 0;
|
||||
this.APShortCode = 0;
|
||||
this.APIPAddress = 0;
|
||||
this.isProxy = false;
|
||||
this.proxyProcess = null;
|
||||
|
||||
this.sync = function(...args){
|
||||
process.nextTick(()=>{
|
||||
for (const name of args) {
|
||||
CLIENT_UPDATE_PROP(this.id, name, this[name]);
|
||||
}
|
||||
})
|
||||
};
|
||||
};
|
||||
/**
|
||||
* @type {Map<string, Client>}
|
||||
|
|
@ -47,6 +59,7 @@ Client.prototype.peerRequest = function(client){
|
|||
let info = {};
|
||||
this.info.forEach((value, name) => info[name] = value);
|
||||
this.pairs.add(client.id);
|
||||
this.sync('pairs');
|
||||
client.send([
|
||||
{ from: this.id },
|
||||
'request/pair'
|
||||
|
|
@ -152,6 +165,7 @@ Client.prototype.getSucureClients = function()
|
|||
*/
|
||||
Client.prototype.acceptPeerRequest = function(client){
|
||||
this.pairs.add(client.id);
|
||||
this.sync('pairs');
|
||||
client.send([{
|
||||
from: this.id
|
||||
},'accepted/pair']);
|
||||
|
|
@ -162,6 +176,7 @@ Client.prototype.getSucureClients = function()
|
|||
Client.prototype.rejectPeerRequest = function(client){
|
||||
this.pairs.delete(client.id);
|
||||
client.pairs.delete(this.id);
|
||||
this.sync('pairs');
|
||||
client.send([{
|
||||
from: this.id
|
||||
},'end/pair']);
|
||||
|
|
@ -185,6 +200,11 @@ Client.prototype.pairList = function(){
|
|||
};
|
||||
|
||||
Client.prototype.send = function(obj){
|
||||
if(this.isProxy)
|
||||
{
|
||||
CLIENT_SEND_MESSAGE(this.id, obj, this.proxyProcess)
|
||||
}else{
|
||||
stats.ws_sended_packs++;
|
||||
if(this.socket.connected){
|
||||
this.socket.sendUTF(JSON.stringify(obj),err => {
|
||||
if(err && this.socket)
|
||||
|
|
@ -195,6 +215,7 @@ Client.prototype.send = function(obj){
|
|||
}else{
|
||||
console.error("Bağlantısı kopmuş yazma işlemi")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Client.prototype.packWriteable = function(){
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const events = new Map();
|
||||
|
||||
function on(event, callback) {
|
||||
if (!events.has(event)) {
|
||||
events.set(event, []);
|
||||
}
|
||||
events.get(event).push(callback);
|
||||
}
|
||||
|
||||
function emit(event, ...args) {
|
||||
if (events.has(event)) {
|
||||
for (const callback of events.get(event)) {
|
||||
try {
|
||||
callback(...args);
|
||||
} catch (error) {
|
||||
console.error(`Event error [${event}]:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function once(event, callback) {
|
||||
const wrapper = (...args) => {
|
||||
off(event, wrapper);
|
||||
callback(...args);
|
||||
};
|
||||
on(event, wrapper);
|
||||
}
|
||||
|
||||
function off(event, callback) {
|
||||
if (events.has(event)) {
|
||||
const callbacks = events.get(event);
|
||||
const index = callbacks.indexOf(callback);
|
||||
if (index > -1) {
|
||||
callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeAllListeners(event) {
|
||||
if (event) {
|
||||
events.delete(event);
|
||||
} else {
|
||||
events.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function listenerCount(event) {
|
||||
return events.has(event) ? events.get(event).length : 0;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
on,
|
||||
emit,
|
||||
once,
|
||||
off,
|
||||
removeAllListeners,
|
||||
listenerCount,
|
||||
events
|
||||
};
|
||||
|
|
@ -4,40 +4,52 @@ let http = require("http");
|
|||
let express = require("express");
|
||||
let compression = require("compression");
|
||||
let {resolve} = require("path");
|
||||
let auth = require("express-basic-auth");
|
||||
|
||||
const { termoutput } = require("./config");
|
||||
|
||||
let server = http.createServer();
|
||||
const stats = require("./stats");
|
||||
let app = express();
|
||||
app.use(compression({ level: 9 }));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
let server = http.createServer(app);
|
||||
server.addListener("request", app);
|
||||
app.use(compression({
|
||||
level: 9
|
||||
}));
|
||||
|
||||
server.listen(7707,'0.0.0.0',() => {
|
||||
termoutput && console.log("HTTP Service Running...");
|
||||
});
|
||||
|
||||
server.on("error", (err) => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
server.addListener("error",(err)=> {
|
||||
console.err(err)
|
||||
})
|
||||
exports.http = server;
|
||||
|
||||
const apiRouter = require("./api");
|
||||
app.use("/api", apiRouter);
|
||||
|
||||
app.get("/script", (req, res) => {
|
||||
res.sendFile(resolve("./script/index.js"));
|
||||
app.get("/script",(request, response)=>{
|
||||
response.sendFile(resolve("./script/index.js"))
|
||||
});
|
||||
|
||||
app.use(express.static(resolve("./public")));
|
||||
app.use("/script", express.static(resolve("./script")));
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.sendFile(resolve("./script/index.js"));
|
||||
app.get("/test",(request, response)=>{
|
||||
response.sendFile(resolve("./script/test.html"))
|
||||
});
|
||||
|
||||
app.get("*", (req, res) => {
|
||||
res.sendFile(resolve("./script/status.xml"));
|
||||
app.get("/index.js.map",(request, response)=>{
|
||||
response.sendFile(resolve("./script/index.js.map"))
|
||||
});
|
||||
app.get("/stream",(request, response)=>{
|
||||
response.sendFile(resolve("./public/index.html"))
|
||||
});
|
||||
app.get("/",(request, response)=>{
|
||||
response.sendFile(resolve("./script/index.html"))
|
||||
});
|
||||
app.post("/stats",(request, response)=>{
|
||||
response.json(stats.others);
|
||||
});
|
||||
app.get("/console",(request, response)=>{
|
||||
response.sendFile(resolve("./console/index.html"))
|
||||
});
|
||||
app.get("/console/lib.js",(request, response)=>{
|
||||
response.sendFile(resolve("./console/lib.js"))
|
||||
});
|
||||
app.use("/stream",express.static(resolve("./public")));
|
||||
app.use("/console/",express.static(resolve("./console")));
|
||||
|
||||
app.get("*",(request, response)=>{
|
||||
response.sendFile(resolve("./script/status.xml"))
|
||||
});
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
|
||||
process.on('message',data => {
|
||||
const { Client } = require("./Client.js");
|
||||
const { Room } = require("./Services/Room");
|
||||
switch(data.type)
|
||||
{
|
||||
case "CLIENT_CREATED":{
|
||||
slog("CLIENT_CREATED");
|
||||
let client = new Client();
|
||||
client.isProxy = true;
|
||||
client.proxyProcess = data.pid;
|
||||
client.id = data.uuid;
|
||||
if(Client.clients.has(client.id))
|
||||
{
|
||||
console.error("IPC: Zaten var olan kullanıcı oluşturuluyor")
|
||||
}else{
|
||||
Client.clients.set(client.id, client);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "CLIENT_UPDATE_PROP":{
|
||||
|
||||
data.value = transformDeserialization(data.value, data.typing);
|
||||
|
||||
slog("CLIENT_UPDATE_PROP");
|
||||
let client = Client.clients.get(data.uuid);
|
||||
client[data.name] = data.value;
|
||||
break;
|
||||
}
|
||||
case "CLIENT_SEND_MESSAGE":{
|
||||
//slog("CLIENT_SEND_MESSAGE");
|
||||
let client = Client.clients.get(data.uuid);
|
||||
if(client)
|
||||
{
|
||||
if(client.isProxy != true)
|
||||
{
|
||||
client.send(data.message)
|
||||
}else{
|
||||
console.error("IPC: Proxy olmayan bir client için IPC mesajı alındı")
|
||||
}
|
||||
}else{
|
||||
console.error("IPC: Olmayan bir kullanıcı için mesaj gönderiliyor")
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "CLIENT_DESTROY":{
|
||||
slog("CLIENT_DESTROY");
|
||||
if(Client.clients.has(data.uuid))
|
||||
{
|
||||
Client.clients.delete(data.uuid);
|
||||
}else{
|
||||
console.error("IPC: Olmayan bir kullanıcı için silme gerçekleştiriliyor")
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "ROOM_CREATED":{
|
||||
slog("ROOM_CREATED");
|
||||
let room = Room.fromJSON(data.value);
|
||||
Room.rooms.set(room.id, room);
|
||||
break;
|
||||
}
|
||||
case "ROOM_UPDATE_PROP":{
|
||||
|
||||
data.value = transformDeserialization(data.value, data.typing);
|
||||
|
||||
|
||||
slog("ROOM_UPDATE_PROP");
|
||||
let room = Room.rooms.get(data.uuid);
|
||||
room[data.name] = data.value;
|
||||
break;
|
||||
}
|
||||
case "ROOM_JOIN_CLIENT":{
|
||||
slog("ROOM_JOIN_CLIENT");
|
||||
let room = Room.rooms.get(data.uuid);
|
||||
let client = Client.clients.get(data.client);
|
||||
if(room && client)
|
||||
{
|
||||
client.rooms.add(room.id);
|
||||
room.clients.set(client.id, client);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "ROOM_EJECT_CLIENT":{
|
||||
slog("ROOM_EJECT_CLIENT");
|
||||
let room = Room.rooms.get(data.uuid);
|
||||
let client = Client.clients.get(data.client);
|
||||
if(room && client)
|
||||
{
|
||||
client.rooms.delete(room.id);
|
||||
room.clients.delete(client.id, client);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "ROOM_DESTROY":{
|
||||
slog("ROOM_DESTROY");
|
||||
Room.rooms.delete(data.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function CLIENT_CREATED(uuid)
|
||||
{
|
||||
mlog("CLIENT_CREATED");
|
||||
process.send({
|
||||
type:'CLIENT_CREATED',
|
||||
uuid: uuid
|
||||
})
|
||||
};
|
||||
function CLIENT_UPDATE_PROP(uuid, name, value)
|
||||
{
|
||||
mlog("CLIENT_UPDATE_PROP");
|
||||
|
||||
let typing = value.__proto__.constructor.name;
|
||||
value = transformSerialization(value);
|
||||
|
||||
process.send({
|
||||
type:'CLIENT_UPDATE_PROP',
|
||||
uuid: uuid,
|
||||
name,
|
||||
value,
|
||||
typing
|
||||
})
|
||||
};
|
||||
function CLIENT_SEND_MESSAGE(uuid, message, clusterPid)
|
||||
{
|
||||
mlog("CLIENT_SEND_MESSAGE");
|
||||
process.send({
|
||||
type:'CLIENT_SEND_MESSAGE',
|
||||
uuid: uuid,
|
||||
message,
|
||||
process:clusterPid
|
||||
})
|
||||
};
|
||||
function CLIENT_DESTROY(uuid)
|
||||
{
|
||||
mlog("CLIENT_DESTROY");
|
||||
process.send({
|
||||
type:'CLIENT_DESTROY',
|
||||
uuid: uuid
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
function ROOM_CREATED(room)
|
||||
{
|
||||
mlog("ROOM_CREATED");
|
||||
let raw = room.toJSON(true);
|
||||
process.send({
|
||||
type:'ROOM_CREATED',
|
||||
value: raw
|
||||
})
|
||||
};
|
||||
|
||||
function ROOM_UPDATE_PROP(uuid, name, value)
|
||||
{
|
||||
mlog("ROOM_UPDATE_PROP");
|
||||
|
||||
let typing = value.__proto__.constructor.name;
|
||||
value = transformSerialization(value);
|
||||
|
||||
process.send({
|
||||
type:'ROOM_UPDATE_PROP',
|
||||
uuid: uuid,
|
||||
name,
|
||||
value,
|
||||
typing
|
||||
})
|
||||
};
|
||||
|
||||
function ROOM_JOIN_CLIENT(uuid, client)
|
||||
{
|
||||
mlog("ROOM_JOIN_CLIENT");
|
||||
process.send({
|
||||
type:'ROOM_JOIN_CLIENT',
|
||||
uuid: uuid,
|
||||
client
|
||||
})
|
||||
};
|
||||
function ROOM_EJECT_CLIENT(uuid, client)
|
||||
{
|
||||
mlog("ROOM_EJECT_CLIENT");
|
||||
process.send({
|
||||
type:'ROOM_EJECT_CLIENT',
|
||||
uuid: uuid,
|
||||
client
|
||||
})
|
||||
};
|
||||
|
||||
function ROOM_DESTROY(room)
|
||||
{
|
||||
mlog("ROOM_DESTROY");
|
||||
process.send({
|
||||
type:'ROOM_DESTROY',
|
||||
value: room.id
|
||||
})
|
||||
};
|
||||
|
||||
function mlog(command)
|
||||
{
|
||||
return;
|
||||
console.log("M",process.pid, command)
|
||||
}
|
||||
function slog(command)
|
||||
{
|
||||
return;
|
||||
console.log("S",process.pid, command)
|
||||
}
|
||||
|
||||
function transformSerialization(value)
|
||||
{
|
||||
switch(value.__proto__.constructor.name)
|
||||
{
|
||||
case "Map":{
|
||||
return [...value];
|
||||
}
|
||||
case "Set":{
|
||||
return [...value];
|
||||
}
|
||||
}
|
||||
}
|
||||
function transformDeserialization(value,type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case "Map":{
|
||||
return new Map(value);
|
||||
}
|
||||
case "Set":{
|
||||
return new Set(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exports.CLIENT_CREATED = CLIENT_CREATED;
|
||||
exports.CLIENT_UPDATE_PROP = CLIENT_UPDATE_PROP;
|
||||
exports.CLIENT_DESTROY = CLIENT_DESTROY;
|
||||
exports.CLIENT_SEND_MESSAGE = CLIENT_SEND_MESSAGE;
|
||||
exports.ROOM_CREATED = ROOM_CREATED;
|
||||
exports.ROOM_UPDATE_PROP = ROOM_UPDATE_PROP;
|
||||
exports.ROOM_JOIN_CLIENT = ROOM_JOIN_CLIENT;
|
||||
exports.ROOM_EJECT_CLIENT = ROOM_EJECT_CLIENT;
|
||||
exports.ROOM_DESTROY = ROOM_DESTROY;
|
||||
exports.mlog = mlog;
|
||||
exports.slog = slog;
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const handlers = new Map();
|
||||
|
||||
function register(type, handler) {
|
||||
handlers.set(type, handler);
|
||||
}
|
||||
|
||||
function handle(client, message) {
|
||||
const { type } = message;
|
||||
|
||||
if (!type) {
|
||||
return { status: 'fail', message: 'MISSING_TYPE' };
|
||||
}
|
||||
|
||||
const handler = handlers.get(type);
|
||||
|
||||
if (!handler) {
|
||||
return { status: 'fail', message: 'UNKNOWN_TYPE' };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = handler(client, message);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`Handler error [${type}]:`, error);
|
||||
return { status: 'fail', message: 'HANDLER_ERROR', error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
function unregister(type) {
|
||||
handlers.delete(type);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
handlers.clear();
|
||||
}
|
||||
|
||||
function hasHandler(type) {
|
||||
return handlers.has(type);
|
||||
}
|
||||
|
||||
function listHandlers() {
|
||||
return [...handlers.keys()];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
register,
|
||||
handle,
|
||||
unregister,
|
||||
clear,
|
||||
hasHandler,
|
||||
listHandlers
|
||||
};
|
||||
|
|
@ -1,175 +1,252 @@
|
|||
"use strict";
|
||||
|
||||
const { Client } = require("../Client.js");
|
||||
const { on, emit, register } = require("../WebSocket");
|
||||
let {addService, addListener} = require("../WebSocket.js");
|
||||
|
||||
on('disconnect', (xclient) => {
|
||||
addListener('disconnect',(global, xclient)=>{
|
||||
const {intersection, pairs} = xclient.getSucureClients();
|
||||
|
||||
for (const [clientid, client] of intersection) {
|
||||
client?.send([{ id: xclient.id }, "peer/disconnect"]);
|
||||
for (const [clientid, client] of intersection)
|
||||
{
|
||||
client?.send([
|
||||
{
|
||||
id: xclient.id
|
||||
},
|
||||
"peer/disconnect"
|
||||
])
|
||||
}
|
||||
|
||||
for (const [id, peer] of pairs) {
|
||||
for (const [id, peer] of pairs)
|
||||
{
|
||||
peer?.pairs.delete(xclient.id);
|
||||
xclient.pairs.delete(id);
|
||||
}
|
||||
});
|
||||
|
||||
register('auth/pair-system', (client, msg) => {
|
||||
if (msg.value == 'everybody') {
|
||||
addService(({
|
||||
client,
|
||||
message,
|
||||
end,
|
||||
next
|
||||
})=>{
|
||||
let {type,to,value,name} = message;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case "auth/pair-system":{
|
||||
if(value == 'everybody')
|
||||
{
|
||||
client.requiredPair = true;
|
||||
return { status: 'success' };
|
||||
end({status:"success"});
|
||||
client.sync('requiredPair');
|
||||
}
|
||||
if (msg.value == 'disable') {
|
||||
if(value == 'disable')
|
||||
{
|
||||
client.requiredPair = false;
|
||||
return { status: 'success' };
|
||||
end({status:"success"});
|
||||
client.sync('requiredPair');
|
||||
//CLIENT_UPDATE_PROP(client.id, 'requiredPair', client.requiredPair);
|
||||
}
|
||||
return { status: 'fail', message: 'INVALID_VALUE' };
|
||||
});
|
||||
|
||||
register('my/socketid', (client, msg) => {
|
||||
return client.id;
|
||||
});
|
||||
|
||||
register('auth/public', (client, msg) => {
|
||||
break;
|
||||
}
|
||||
case "my/socketid":{
|
||||
end(client.id);
|
||||
break;
|
||||
}
|
||||
case 'auth/public':{
|
||||
client.requiredPair = false;
|
||||
return { value: 'success', mode: 'public' };
|
||||
});
|
||||
|
||||
register('auth/private', (client, msg) => {
|
||||
client.sync('requiredPair');
|
||||
return end({
|
||||
value: 'success',
|
||||
mode: 'public'
|
||||
})
|
||||
}
|
||||
case 'auth/private':{
|
||||
client.requiredPair = true;
|
||||
return { value: 'success', mode: 'private' };
|
||||
});
|
||||
|
||||
register('request/pair', (client, msg) => {
|
||||
const { to } = msg;
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||
client.sync('requiredPair');
|
||||
return end({
|
||||
value: 'success',
|
||||
mode: 'private'
|
||||
})
|
||||
}
|
||||
|
||||
const pairclient = Client.clients.get(to);
|
||||
|
||||
if (pairclient.pairs.has(client.id)) {
|
||||
return { status: 'success', message: 'ALREADY-PAIRED' };
|
||||
case 'request/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: 'ALREADY-PAIRED'
|
||||
})
|
||||
}
|
||||
|
||||
if (client.pairs.has(to)) {
|
||||
return { status: 'fail', message: 'ALREADY-REQUESTED' };
|
||||
}
|
||||
|
||||
if(client.pairs.has(to))
|
||||
{
|
||||
return end({
|
||||
status: 'fail',
|
||||
message: 'ALREADY-REQUESTED'
|
||||
})
|
||||
};
|
||||
end({
|
||||
status: 'success',
|
||||
message: 'REQUESTED'
|
||||
})
|
||||
client.peerRequest(pairclient);
|
||||
return { status: 'success', message: 'REQUESTED' };
|
||||
});
|
||||
|
||||
register('accept/pair', (client, msg) => {
|
||||
const { to } = msg;
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||
break;
|
||||
}
|
||||
|
||||
const pairclient = Client.clients.get(to);
|
||||
|
||||
if (pairclient.pairs.has(client.id)) {
|
||||
return { status: 'success', message: 'ALREADY-PAIRED' };
|
||||
case 'accept/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: 'ALREADY-PAIRED'
|
||||
})
|
||||
}
|
||||
|
||||
if (!client.pairs.has(to)) {
|
||||
return { status: 'fail', message: 'NOT_REQUESTED_PAIR' };
|
||||
if(!client.pairs.has(to))
|
||||
{
|
||||
return end({
|
||||
status: 'fail',
|
||||
message: 'NOT-REQUESTED-PAIR'
|
||||
})
|
||||
}
|
||||
|
||||
client.acceptPeerRequest(pairclient);
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('reject/pair', (client, msg) => {
|
||||
const { to } = msg;
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||
break;
|
||||
}
|
||||
|
||||
const pairclient = Client.clients.get(to);
|
||||
|
||||
if (pairclient.pairs.has(client.id)) {
|
||||
return { status: 'success', message: 'ALREADY-PAIRED' };
|
||||
case 'reject/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: 'ALREADY-PAIRED'
|
||||
})
|
||||
}
|
||||
|
||||
if (!client.pairs.has(to)) {
|
||||
return { status: 'fail', message: 'NOT_REQUESTED_PAIR' };
|
||||
if(!client.pairs.has(to))
|
||||
{
|
||||
return end({
|
||||
status: 'fail',
|
||||
message: 'NOT-REQUESTED-PAIR'
|
||||
})
|
||||
}
|
||||
|
||||
client.rejectPeerRequest(pairclient);
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('end/pair', (client, msg) => {
|
||||
const { to } = msg;
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||
break;
|
||||
}
|
||||
|
||||
const pairclient = Client.clients.get(to);
|
||||
|
||||
if (!pairclient.pairs.has(client.id)) {
|
||||
return { status: 'success', message: 'NOT_PAIRED' };
|
||||
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);
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('pair/list', (client, msg) => {
|
||||
return { type: 'pair/list', value: client.pairList() };
|
||||
});
|
||||
|
||||
register('is/reachable', (client, msg) => {
|
||||
const { to } = msg;
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
const otherPeer = Client.clients.get(to);
|
||||
|
||||
if (otherPeer.requiredPair && !otherPeer.pairs.has(to)) {
|
||||
return false;
|
||||
case 'pair/list':{
|
||||
end({
|
||||
type:'pair/list',
|
||||
value: client.pairList()
|
||||
})
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
register('auth/info', (client, msg) => {
|
||||
const { name, value } = msg;
|
||||
|
||||
case 'is/reachable':{
|
||||
if(Client.clients.has(to))
|
||||
{
|
||||
let otherPeer = Client.clients.get(to);
|
||||
if(otherPeer.requiredPair && !otherPeer.pairs.has(to))
|
||||
{
|
||||
end(false);
|
||||
}else{
|
||||
end(true);
|
||||
}
|
||||
}else{
|
||||
end(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'auth/info':{
|
||||
client.info.set(name, value);
|
||||
|
||||
const clients = client.getSucureClients();
|
||||
|
||||
for (const [, spair] of clients.pairs) {
|
||||
spair.send([{ from: client.id, name, value }, "pair/info"]);
|
||||
let clients = client.getSucureClients();
|
||||
for (const [,spair] of clients.pairs)
|
||||
{
|
||||
spair.send([{
|
||||
from: client.id,
|
||||
name,
|
||||
value
|
||||
},"pair/info"]);
|
||||
};
|
||||
for (const [,spair] of clients.roompairs)
|
||||
{
|
||||
spair.send([{
|
||||
from: client.id,
|
||||
name,
|
||||
value
|
||||
},"pair/info"]);
|
||||
};
|
||||
process.send({
|
||||
type: 'AUTH/INFO',
|
||||
uuid: client.id,
|
||||
name,
|
||||
value
|
||||
});
|
||||
return end({
|
||||
status: 'success'
|
||||
});
|
||||
}
|
||||
case 'peer/info':{
|
||||
if(client.isSecure(message.peer))
|
||||
{
|
||||
let info = {};
|
||||
let peer = Client.clients.get(message.peer);
|
||||
peer.info.forEach((value, name) => info[name] = value);
|
||||
return end({
|
||||
status: "success",
|
||||
info
|
||||
});
|
||||
}else{
|
||||
return end({
|
||||
status: "fail",
|
||||
message: "unaccessible user"
|
||||
});
|
||||
}
|
||||
}
|
||||
default:{
|
||||
next();
|
||||
}
|
||||
|
||||
for (const [, spair] of clients.roompairs) {
|
||||
spair.send([{ from: client.id, name, value }, "pair/info"]);
|
||||
}
|
||||
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('peer/info', (client, msg) => {
|
||||
const { peer } = msg;
|
||||
|
||||
if (!client.isSecure(peer)) {
|
||||
return { status: "fail", message: "unaccessible user" };
|
||||
process.on('message',({type, uuid, value, name}) => {
|
||||
switch(type)
|
||||
{
|
||||
case "AUTH/INFO":{
|
||||
let client = Client.clients.get(uuid);
|
||||
if(client)
|
||||
{
|
||||
client.info.set(name, value);
|
||||
}
|
||||
|
||||
const peerClient = Client.clients.get(peer);
|
||||
const info = {};
|
||||
peerClient.info.forEach((value, name) => { info[name] = value; });
|
||||
|
||||
return { status: "success", info };
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -1,101 +1,168 @@
|
|||
"use strict";
|
||||
|
||||
const { Client } = require("../Client.js");
|
||||
const { register } = require("../WebSocket");
|
||||
const { Room } = require("./Room");
|
||||
let {randomUUID} = require("crypto");
|
||||
let {addService,addListener} = require("../WebSocket.js");
|
||||
const { Room } = require("./Room.js");
|
||||
|
||||
register('pack/to', (client, msg) => {
|
||||
const { to, pack, handshake } = msg;
|
||||
/*
|
||||
Peer to peer veri aktarımı
|
||||
|
||||
- Kişiden kişiye direkt veri aktarımı
|
||||
- Kişiler arası tünelleme / Kişiler arası özel protokol
|
||||
- Oda katılımcıları içerisinde veri aktarımı
|
||||
- Oda katılımcıları arasında belli kişilere veri aktarımı
|
||||
|
||||
*/
|
||||
|
||||
addService(({
|
||||
client,
|
||||
end,
|
||||
global,
|
||||
message,
|
||||
next,
|
||||
response,
|
||||
messageId
|
||||
}) => {
|
||||
let {type} = message;
|
||||
switch(type)
|
||||
{
|
||||
case "pack/to":{
|
||||
let {to,pack,handshake} = message;
|
||||
|
||||
if(!client.packReadable()){
|
||||
return handshake ? { type: 'fail' } : undefined;
|
||||
console.error("Okunabilir olmayan client bir mesaj iletiyor")
|
||||
handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
}
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
return handshake ? { type: 'fail' } : undefined;
|
||||
}
|
||||
|
||||
const otherPeer = Client.clients.get(to);
|
||||
|
||||
if (otherPeer.requiredPair) {
|
||||
if (!otherPeer.pairs.has(to)) {
|
||||
return handshake ? { type: 'fail' } : undefined;
|
||||
if(Client.clients.has(to))
|
||||
{
|
||||
let otherPeer = Client.clients.get(to);
|
||||
if(otherPeer.requiredPair)
|
||||
{
|
||||
if(!otherPeer.pairs.has(to))
|
||||
{
|
||||
console.error("Client, güvenilir olmayan bir cliente mesaj iletiyor")
|
||||
return handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
}
|
||||
}else{
|
||||
if (!otherPeer.pairs.has(to)) {
|
||||
if(!otherPeer.pairs.has(to))
|
||||
{
|
||||
otherPeer.pairs.add(client.id);
|
||||
client.pairs.add(otherPeer.id);
|
||||
}
|
||||
}
|
||||
|
||||
if(!otherPeer.packWriteable()){
|
||||
return handshake ? { type: 'fail' } : undefined;
|
||||
console.error("Client, yazılabilir olmayan bir cliente mesaj iletiyor")
|
||||
handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
}
|
||||
|
||||
otherPeer.send([{ from: client.id, pack }, 'pack']);
|
||||
|
||||
return handshake ? { type: 'success' } : undefined;
|
||||
});
|
||||
|
||||
register('request/to', (client, msg) => {
|
||||
const { to, pack } = msg;
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
return;
|
||||
otherPeer.send([{
|
||||
from: client.id,
|
||||
pack: pack
|
||||
}, 'pack']);
|
||||
handshake && end({
|
||||
type: 'success'
|
||||
})
|
||||
}else{
|
||||
console.error("Client, olmayan bir cliente mesaj iletiyor")
|
||||
handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
}
|
||||
|
||||
const otherPeer = Client.clients.get(to);
|
||||
|
||||
if (otherPeer.requiredPair) {
|
||||
if (!otherPeer.pairs.has(to)) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
case "request/to":{
|
||||
let {to,pack} = message;
|
||||
if(Client.clients.has(to))
|
||||
{
|
||||
let otherPeer = Client.clients.get(to);
|
||||
if(otherPeer.requiredPair)
|
||||
{
|
||||
if(!otherPeer.pairs.has(to))
|
||||
{
|
||||
console.error("Client, güvenilir olmayan bir clientden veri istiyor")
|
||||
return handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
}
|
||||
}else{
|
||||
otherPeer.pairs.add(client.id);
|
||||
client.pairs.add(otherPeer.id);
|
||||
}
|
||||
|
||||
otherPeer.send([{ from: client.id, pack }, 'request']);
|
||||
});
|
||||
|
||||
register('response/to', (client, msg) => {
|
||||
const { to, pack, id } = msg;
|
||||
|
||||
if (!Client.clients.has(to)) {
|
||||
otherPeer.send([{
|
||||
from: client.id,
|
||||
pack: pack,
|
||||
id:messageId
|
||||
}, 'request']);
|
||||
}else console.error("request/to error not finded Peer");
|
||||
break;
|
||||
}
|
||||
case "response/to":{
|
||||
let {to,pack, id} = message;
|
||||
if(Client.clients.has(to))
|
||||
{
|
||||
let otherPeer = Client.clients.get(to);
|
||||
if(otherPeer.requiredPair && !otherPeer.pairs.has(to))
|
||||
{
|
||||
console.error("response istenen peer güvenli değil")
|
||||
return;
|
||||
}
|
||||
|
||||
const otherPeer = Client.clients.get(to);
|
||||
|
||||
if (otherPeer.requiredPair && !otherPeer.pairs.has(to)) {
|
||||
return;
|
||||
otherPeer.send([{
|
||||
from: client.id,
|
||||
pack: pack
|
||||
}, id]);
|
||||
}else console.error("response/to error not finded Peer");
|
||||
break;
|
||||
}
|
||||
case "pack/room":{
|
||||
let {to,pack, handshake,wom} = message;
|
||||
|
||||
otherPeer.send([{ from: client.id, pack }, id]);
|
||||
});
|
||||
|
||||
register('pack/room', (client, msg) => {
|
||||
const { to, pack, handshake, wom } = msg;
|
||||
|
||||
if(!client.packReadable()){
|
||||
return handshake ? { type: 'fail' } : undefined;
|
||||
console.error("Client paketi okumak için müsait değil")
|
||||
handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
}
|
||||
|
||||
if (!Room.rooms.has(to)) {
|
||||
return handshake ? { type: 'fail' } : undefined;
|
||||
}
|
||||
|
||||
if (!client.rooms.has(to)) {
|
||||
return handshake ? { type: 'fail' } : undefined;
|
||||
}
|
||||
|
||||
const room = Room.rooms.get(to);
|
||||
|
||||
room.send(
|
||||
[{ from: to, pack, sender: client.id }, 'pack/room'],
|
||||
wom ? client.id : undefined,
|
||||
c => c.packWriteable()
|
||||
if(Room.rooms.has(to))
|
||||
{
|
||||
if(!client.rooms.has(to))
|
||||
{
|
||||
console.error("Client katılmadığı bir odaya mesaj iletiyor")
|
||||
return handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
};
|
||||
Room.rooms.get(to).send(
|
||||
[
|
||||
{
|
||||
from: to,
|
||||
pack: pack,
|
||||
sender: client.id
|
||||
}, 'pack/room'
|
||||
],
|
||||
wom ? client.id : void 0,
|
||||
client => client.packWriteable()
|
||||
);
|
||||
|
||||
return handshake ? { type: 'success' } : undefined;
|
||||
handshake && end({
|
||||
type: 'success'
|
||||
})
|
||||
}else{
|
||||
console.error("Olmayan oda için veri gönderme isteniyor")
|
||||
handshake && end({
|
||||
type: 'fail'
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
next();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -1,70 +1,88 @@
|
|||
"use strict";
|
||||
|
||||
const { Client } = require("../Client");
|
||||
const { on, register } = require("../WebSocket");
|
||||
let {addService, addListener} = require("../WebSocket.js");
|
||||
|
||||
class APNumber{
|
||||
/**
|
||||
* @type {Map<Number, Client>}
|
||||
*/
|
||||
static busyNumbers = new Map();
|
||||
|
||||
static lock(client) {
|
||||
/**
|
||||
* @type {number}
|
||||
* @param {Client} client
|
||||
*/
|
||||
static lock(client)
|
||||
{
|
||||
let c = 24;
|
||||
while(true){
|
||||
if (!APNumber.busyNumbers.has(c)) {
|
||||
if(!APNumber.busyNumbers.has(c))
|
||||
{
|
||||
APNumber.busyNumbers.set(c,client);
|
||||
process.send({
|
||||
type: 'AP_NUMBER/LOCK',
|
||||
uuid: client.id,
|
||||
value: c
|
||||
});
|
||||
})
|
||||
return c;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
static release(num) {
|
||||
/**
|
||||
* @param {number} num
|
||||
*/
|
||||
static release(num)
|
||||
{
|
||||
process.send({
|
||||
type: 'AP_NUMBER/RELEASE',
|
||||
uuid: APNumber.busyNumbers.get(num).id,
|
||||
value: num
|
||||
});
|
||||
})
|
||||
APNumber.busyNumbers.delete(num);
|
||||
}
|
||||
|
||||
static whois(num){
|
||||
return APNumber.busyNumbers.get(num)?.id;
|
||||
}
|
||||
}
|
||||
|
||||
class APShortCode{
|
||||
/**
|
||||
* @type {Map<string, Client>}
|
||||
*/
|
||||
static busyCodes = new Map();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Client} client
|
||||
* @returns
|
||||
*/
|
||||
static lock(client){
|
||||
let firstLetter = new ShortCodeLetter();
|
||||
let secondLetter = new ShortCodeLetter();
|
||||
let thirdLetter = new ShortCodeLetter();
|
||||
|
||||
while (1) {
|
||||
while(1)
|
||||
{
|
||||
let code = [firstLetter.code,secondLetter.code,thirdLetter.code].join('');
|
||||
if (!APShortCode.busyCodes.has(code)) {
|
||||
if(APShortCode.busyCodes.has(code) == false)
|
||||
{
|
||||
APShortCode.busyCodes.set(code, client);
|
||||
process.send({
|
||||
type: 'AP_SHORTCODE/LOCK',
|
||||
uuid: APShortCode.busyCodes.get(code).id,
|
||||
value: code
|
||||
});
|
||||
})
|
||||
return code;
|
||||
}
|
||||
|
||||
if (!thirdLetter.end()) {
|
||||
thirdLetter.next();
|
||||
if(!thirdLetter.end())
|
||||
{
|
||||
thirdLetter.next()
|
||||
}else{
|
||||
thirdLetter.reset();
|
||||
if (!secondLetter.end()) {
|
||||
thirdLetter.reset()
|
||||
if(!secondLetter.end())
|
||||
{
|
||||
secondLetter.next();
|
||||
}else{
|
||||
secondLetter.reset();
|
||||
if (!firstLetter.end()) {
|
||||
if(!firstLetter.end())
|
||||
{
|
||||
firstLetter.next();
|
||||
}else{
|
||||
break;
|
||||
|
|
@ -73,197 +91,364 @@ class APShortCode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static release(code){
|
||||
if (APShortCode.busyCodes.has(code)) {
|
||||
if(APShortCode.busyCodes.has(code))
|
||||
{
|
||||
process.send({
|
||||
type: 'AP_SHORTCODE/RELEASE',
|
||||
uuid: APShortCode.busyCodes.get(code).id,
|
||||
value: code
|
||||
});
|
||||
})
|
||||
APShortCode.busyCodes.delete(code);
|
||||
}
|
||||
}
|
||||
|
||||
static whois(num){
|
||||
return APShortCode.busyCodes.get(num)?.id;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
class ShortCodeLetter{
|
||||
chars = 'ABCDEFGHIKLMNOPRSTVXYZ'.split('');
|
||||
now = 0;
|
||||
code = 'A';
|
||||
|
||||
next(){
|
||||
this.now++;
|
||||
this.now++
|
||||
this.code = this.chars.at(this.now);
|
||||
return this.code;
|
||||
}
|
||||
|
||||
reset(){
|
||||
this.now = 0;
|
||||
this.code = 'A';
|
||||
}
|
||||
|
||||
end(){
|
||||
return !this.chars.at(this.now + 1);
|
||||
return !this.chars.at(this.now + 1)
|
||||
}
|
||||
}
|
||||
|
||||
class APIPAddress{
|
||||
/**
|
||||
* @type {Map<[number,number,number,number], Client>}
|
||||
*/
|
||||
static busyIP = new Map();
|
||||
|
||||
/**
|
||||
* @param {Client} client
|
||||
*/
|
||||
static lock(client){
|
||||
let A = 10, B = 0, C = 0, D = 1;
|
||||
|
||||
while (1) {
|
||||
let A = 10;
|
||||
let B = 0;
|
||||
let C = 0;
|
||||
let D = 1;
|
||||
while(1)
|
||||
{
|
||||
let code = [A,B,C,D].join('.');
|
||||
if (!APIPAddress.busyIP.has(code)) {
|
||||
if(APIPAddress.busyIP.has(code) == false)
|
||||
{
|
||||
APIPAddress.busyIP.set(code, client);
|
||||
process.send({
|
||||
type: 'AP_IPADDRESS/LOCK',
|
||||
uuid: APIPAddress.busyIP.get(code).id,
|
||||
value: code
|
||||
});
|
||||
})
|
||||
return code;
|
||||
}
|
||||
|
||||
if (D != 255) { D++; continue; }
|
||||
if(D != 255)
|
||||
{
|
||||
D++;
|
||||
continue;
|
||||
}else{
|
||||
D = 0;
|
||||
if (C != 255) { C++; continue; }
|
||||
}
|
||||
if(C != 255)
|
||||
{
|
||||
C++;
|
||||
continue;
|
||||
}else{
|
||||
C = 0;
|
||||
if (B != 255) { B++; continue; }
|
||||
}
|
||||
if(B != 255)
|
||||
{
|
||||
B++;
|
||||
continue;
|
||||
}else{
|
||||
B = 0;
|
||||
if (A != 255) { A++; continue; }
|
||||
}
|
||||
if(A != 255)
|
||||
{
|
||||
A++;
|
||||
continue;
|
||||
}else{
|
||||
A = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static release(code){
|
||||
if (APIPAddress.busyIP.has(code)) {
|
||||
if(APIPAddress.busyIP.has(code))
|
||||
{
|
||||
process.send({
|
||||
type: 'AP_IPADDRESS/RELEASE',
|
||||
uuid: APIPAddress.busyIP.get(code).id,
|
||||
value: code
|
||||
});
|
||||
})
|
||||
APIPAddress.busyIP.delete(code);
|
||||
}
|
||||
}
|
||||
|
||||
static whois(num){
|
||||
return APIPAddress.busyIP.get(num)?.id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.APNumber = APNumber;
|
||||
exports.APShortCode = APShortCode;
|
||||
exports.APIPAddress = APIPAddress;
|
||||
|
||||
register('alloc/APIPAddress', (client, msg) => {
|
||||
addService(({
|
||||
client,
|
||||
message,
|
||||
end,
|
||||
next
|
||||
})=>{
|
||||
let {type,whois} = message;
|
||||
switch(type)
|
||||
{
|
||||
case "alloc/APIPAddress":{
|
||||
if(client.APIPAddress) {
|
||||
return { status: 'success', ip: client.APIPAddress };
|
||||
}
|
||||
end({
|
||||
status : "success",
|
||||
ip : client.APIPAddress
|
||||
})
|
||||
};
|
||||
let value = APIPAddress.lock(client);
|
||||
client.APIPAddress = value;
|
||||
return { status: 'success', ip: value };
|
||||
});
|
||||
|
||||
register('alloc/APNumber', (client, msg) => {
|
||||
if (client.APNumber) {
|
||||
return { status: 'success', number: client.APNumber };
|
||||
client.sync('APIPAddress');
|
||||
end({
|
||||
status : "success",
|
||||
ip : value
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "alloc/APNumber":{
|
||||
if(client.APNumber) {
|
||||
end({
|
||||
status : "success",
|
||||
number : client.APNumber
|
||||
})
|
||||
};
|
||||
let value = APNumber.lock(client);
|
||||
client.APNumber = value;
|
||||
return { status: 'success', number: value };
|
||||
});
|
||||
|
||||
register('alloc/APShortCode', (client, msg) => {
|
||||
if (client.APShortCode) {
|
||||
return { status: 'success', code: client.APShortCode };
|
||||
client.sync('APNumber');
|
||||
end({
|
||||
status : "success",
|
||||
number : value
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "alloc/APShortCode":{
|
||||
if(client.APShortCode) {
|
||||
end({
|
||||
status : "success",
|
||||
code : client.APShortCode
|
||||
})
|
||||
};
|
||||
let value = APShortCode.lock(client);
|
||||
client.APShortCode = value;
|
||||
return { status: 'success', code: value };
|
||||
});
|
||||
|
||||
register('realloc/APIPAddress', (client, msg) => {
|
||||
client.sync('APShortCode');
|
||||
end({
|
||||
status : "success",
|
||||
code : value
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "realloc/APIPAddress":{
|
||||
if(client.APIPAddress == 0){
|
||||
return { status: 'fail' };
|
||||
return end({
|
||||
status : "fail"
|
||||
})
|
||||
}
|
||||
APIPAddress.release(client.APIPAddress);
|
||||
let value = APIPAddress.lock(client);
|
||||
return { status: 'success', ip: value };
|
||||
});
|
||||
|
||||
register('realloc/APNumber', (client, msg) => {
|
||||
client.sync('APIPAddress');
|
||||
end({
|
||||
status : "success",
|
||||
ip : value
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "realloc/APNumber":{
|
||||
if(client.APNumber == 0){
|
||||
return { status: 'fail' };
|
||||
return end({
|
||||
status : "fail"
|
||||
})
|
||||
}
|
||||
APNumber.release(client.APNumber);
|
||||
let value = APNumber.lock(client);
|
||||
return { status: 'success', number: value };
|
||||
});
|
||||
|
||||
register('realloc/APShortCode', (client, msg) => {
|
||||
client.sync('APNumber');
|
||||
end({
|
||||
status : "success",
|
||||
number : value
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "realloc/APShortCode":{
|
||||
if(client.APShortCode == 0){
|
||||
return { status: 'fail' };
|
||||
return end({
|
||||
status : "fail"
|
||||
})
|
||||
}
|
||||
APShortCode.release(client.APShortCode);
|
||||
let value = APShortCode.lock(client);
|
||||
return { status: 'success', code: value };
|
||||
});
|
||||
|
||||
register('release/APIPAddress', (client, msg) => {
|
||||
client.sync('APShortCode');
|
||||
end({
|
||||
status : "success",
|
||||
code : value
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "release/APIPAddress":{
|
||||
APIPAddress.release(client.APIPAddress);
|
||||
client.APIPAddress = undefined;
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('release/APNumber', (client, msg) => {
|
||||
client.APIPAddress = void 0;
|
||||
client.sync('APShortCode');
|
||||
end({
|
||||
status : "success"
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "release/APNumber":{
|
||||
APNumber.release(client.APNumber);
|
||||
client.APNumber = undefined;
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('release/APShortCode', (client, msg) => {
|
||||
client.APNumber = void 0;
|
||||
client.sync('APIPAddress');
|
||||
end({
|
||||
status : "success"
|
||||
})
|
||||
break;
|
||||
}
|
||||
case "release/APShortCode":{
|
||||
APShortCode.release(client.APShortCode);
|
||||
client.APShortCode = undefined;
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('whois/APIPAddress', (client, msg) => {
|
||||
let socketId = APIPAddress.whois(msg.whois);
|
||||
if (socketId) {
|
||||
return { status: 'success', socket: socketId };
|
||||
client.APShortCode = void 0;
|
||||
client.sync('APIPAddress');
|
||||
end({
|
||||
status : "success"
|
||||
})
|
||||
break;
|
||||
}
|
||||
return { status: 'fail' };
|
||||
});
|
||||
|
||||
register('whois/APNumber', (client, msg) => {
|
||||
let socketId = APNumber.whois(msg.whois);
|
||||
if (socketId) {
|
||||
return { status: 'success', socket: socketId };
|
||||
case "whois/APIPAddress":{
|
||||
let socketId = APIPAddress.whois(whois);
|
||||
if(socketId)
|
||||
{
|
||||
end({
|
||||
status : "success",
|
||||
socket : socketId
|
||||
})
|
||||
}else{
|
||||
end({
|
||||
status : "fail"
|
||||
})
|
||||
}
|
||||
return { status: 'fail' };
|
||||
});
|
||||
|
||||
register('whois/APShortCode', (client, msg) => {
|
||||
let socketId = APShortCode.whois(msg.whois);
|
||||
if (socketId) {
|
||||
return { status: 'success', socket: socketId };
|
||||
break;
|
||||
}
|
||||
return { status: 'fail' };
|
||||
});
|
||||
case "whois/APNumber":{
|
||||
let socketId = APNumber.whois(whois);
|
||||
if(socketId)
|
||||
{
|
||||
end({
|
||||
status : "success",
|
||||
socket : socketId
|
||||
})
|
||||
}else{
|
||||
end({
|
||||
status : "fail"
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "whois/APShortCode":{
|
||||
let socketId = APShortCode.whois(whois);
|
||||
if(socketId)
|
||||
{
|
||||
end({
|
||||
status : "success",
|
||||
socket : socketId
|
||||
})
|
||||
}else{
|
||||
end({
|
||||
status : "fail"
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
on('disconnect', (client) => {
|
||||
if (client.APIPAddress != 0) {
|
||||
process.on('message',({type, uuid, value}) => {
|
||||
switch(type)
|
||||
{
|
||||
case "AP_NUMBER/LOCK":{
|
||||
let client = Client.clients.get(uuid);
|
||||
APNumber.busyNumbers.set(value, client);
|
||||
if(client)
|
||||
{
|
||||
client.APNumber = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "AP_NUMBER/RELEASE":{
|
||||
APNumber.busyNumbers.delete(value);
|
||||
let client = Client.clients.get(uuid);
|
||||
if(client)
|
||||
{
|
||||
client.APNumber = void 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "AP_SHORTCODE/LOCK":{
|
||||
let client = Client.clients.get(uuid);
|
||||
APShortCode.busyCodes.set(value, client);
|
||||
if(client)
|
||||
{
|
||||
client.APShortCode = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "AP_SHORTCODE/RELEASE":{
|
||||
APShortCode.busyCodes.delete(value);
|
||||
let client = Client.clients.get(uuid);
|
||||
if(client)
|
||||
{
|
||||
client.APShortCode = void 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "AP_IPADDRESS/LOCK":{
|
||||
let client = Client.clients.get(uuid);
|
||||
APIPAddress.busyIP.set(value, client);
|
||||
if(client)
|
||||
{
|
||||
client.APIPAddress = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "AP_IPADDRESS/RELEASE":{
|
||||
APIPAddress.busyIP.delete(value);
|
||||
let client = Client.clients.get(uuid);
|
||||
if(client)
|
||||
{
|
||||
client.APIPAddress = void 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
addListener('disconnect',(global, client)=>{
|
||||
if(client.APIPAddress != 0)
|
||||
{
|
||||
APIPAddress.release(client.APIPAddress);
|
||||
}
|
||||
if (client.APNumber != 0) {
|
||||
if(client.APNumber != 0)
|
||||
{
|
||||
APNumber.release(client.APNumber);
|
||||
}
|
||||
if (client.APShortCode != 0) {
|
||||
if(client.APShortCode != 0)
|
||||
{
|
||||
APShortCode.release(client.APShortCode);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
const { Client } = require("../Client.js");
|
||||
let {randomUUID,createHash} = require("crypto");
|
||||
const joi = require("joi");
|
||||
const { on, register } = require("../WebSocket");
|
||||
let {addService,addListener} = require("../WebSocket.js");
|
||||
const { termoutput } = require("../config.js");
|
||||
const { ROOM_CREATED, ROOM_DESTROY, ROOM_UPDATE_PROP, ROOM_JOIN_CLIENT, ROOM_EJECT_CLIENT } = require("../IPC.js");
|
||||
let term = require("terminal-kit").terminal;
|
||||
const stats = require("../stats");
|
||||
|
||||
function Sha256(update) {
|
||||
function Sha256(update)
|
||||
{
|
||||
return createHash("sha256").update(update).digest("hex");
|
||||
};
|
||||
|
||||
|
|
@ -68,12 +71,22 @@ function Room()
|
|||
* @type {Map<string,any>}
|
||||
*/
|
||||
this.info = new Map();
|
||||
|
||||
this.sync = function(...args){
|
||||
process.nextTick(()=>{
|
||||
for (const name of args) {
|
||||
ROOM_UPDATE_PROP(this.id, name, this[name]);
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {Room} room
|
||||
*/
|
||||
Room.prototype.publish = function(){
|
||||
stats.mwse_rooms++;
|
||||
Room.rooms.set(this.id, this);
|
||||
ROOM_CREATED(this);
|
||||
termoutput && term.green("Room Published ").white(this.name," in ").yellow(this.clients.size).white(" clients")('\n');
|
||||
};
|
||||
/**
|
||||
|
|
@ -186,6 +199,7 @@ Room.prototype.join = function(client){
|
|||
};
|
||||
client.rooms.add(this.id);
|
||||
this.clients.set(client.id, client);
|
||||
ROOM_JOIN_CLIENT(this.id, client.id);
|
||||
termoutput && term.green("Client Room joined ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
|
||||
};
|
||||
Room.prototype.down = function(){
|
||||
|
|
@ -195,6 +209,8 @@ Room.prototype.down = function(){
|
|||
ownerid: this.owner.id
|
||||
},'room/closed']);
|
||||
Room.rooms.delete(this.id);
|
||||
ROOM_DESTROY(this)
|
||||
stats.mwse_rooms--;
|
||||
};
|
||||
/**
|
||||
* @param {Client} client
|
||||
|
|
@ -217,6 +233,7 @@ Room.prototype.eject = function(client){
|
|||
}
|
||||
client.rooms.delete(this.id);
|
||||
this.clients.delete(client.id);
|
||||
ROOM_EJECT_CLIENT(this.id, client.id);
|
||||
|
||||
if(this.clients.size == 0)
|
||||
{
|
||||
|
|
@ -232,7 +249,7 @@ Room.prototype.eject = function(client){
|
|||
*/
|
||||
Room.rooms = new Map();
|
||||
|
||||
on('connect', (client) => {
|
||||
addListener('connect',(global, client)=>{
|
||||
let room = new Room();
|
||||
room.accessType = "private";
|
||||
room.joinType = "notify";
|
||||
|
|
@ -244,12 +261,10 @@ on('connect', (client) => {
|
|||
room.join(client);
|
||||
});
|
||||
|
||||
on('disconnect', (client) => {
|
||||
const room = Room.rooms.get(client.id);
|
||||
if (room) room.eject(client);
|
||||
addListener('disconnect',(global, client)=>{
|
||||
Room.rooms.get(client.id).eject(client);
|
||||
for (const roomId of client.rooms) {
|
||||
const r = Room.rooms.get(roomId);
|
||||
if (r) r.eject(client);
|
||||
Room.rooms.get(roomId).eject(client);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -268,282 +283,466 @@ let CreateRoomVerify = joi.object({
|
|||
autoFetchInfo: joi.boolean().optional(),
|
||||
});
|
||||
|
||||
register('myroom-info', (client, msg) => {
|
||||
addService(({
|
||||
client,
|
||||
end,
|
||||
global,
|
||||
message,
|
||||
next,
|
||||
response
|
||||
})=>{
|
||||
let {type} = message;
|
||||
switch(type)
|
||||
{
|
||||
case 'myroom-info':{
|
||||
let room = Room.rooms.get(client.id);
|
||||
return { status: "success", room: room.toJSON() };
|
||||
});
|
||||
|
||||
register('room-peers', (client, msg) => {
|
||||
const { roomId, filter } = msg;
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: 'fail' };
|
||||
end({
|
||||
status: "success",
|
||||
room: room.toJSON()
|
||||
})
|
||||
break;
|
||||
}
|
||||
const filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
||||
return { status: 'success', peers: filteredPeers.map(i => i.id) };
|
||||
case 'room-peers':{
|
||||
let {roomId,filter} = message;
|
||||
if(Room.rooms.has(roomId))
|
||||
{
|
||||
let filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
||||
end({
|
||||
status: 'success',
|
||||
peers: filteredPeers.map(i => i.id)
|
||||
});
|
||||
|
||||
register('room/peer-count', (client, msg) => {
|
||||
const { roomId, filter } = msg;
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: 'fail' };
|
||||
}else{
|
||||
end({
|
||||
status: 'fail'
|
||||
})
|
||||
}
|
||||
const filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
||||
return { status: 'success', count: filteredPeers.length };
|
||||
break;
|
||||
}
|
||||
case 'room/peer-count':{
|
||||
let {roomId,filter} = message;
|
||||
if(Room.rooms.has(roomId))
|
||||
{
|
||||
let filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
||||
end({
|
||||
status: 'success',
|
||||
count: filteredPeers.length
|
||||
});
|
||||
|
||||
register('room-info', (client, msg) => {
|
||||
const { name } = msg;
|
||||
for (const [roomId, room] of Room.rooms) {
|
||||
if (name == room.name) {
|
||||
return { status: "success", room: room.toJSON() };
|
||||
}else{
|
||||
end({
|
||||
status: 'fail'
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
case 'room-info':{
|
||||
let {name} = message;
|
||||
for (const [roomId,{name:RoomName}] of Room.rooms) {
|
||||
if(name == RoomName)
|
||||
{
|
||||
return end({
|
||||
status : "success",
|
||||
room : Room.rooms.get(roomId).toJSON()
|
||||
})
|
||||
}
|
||||
};
|
||||
end({
|
||||
status : "fail",
|
||||
message : "NOT-FOUND-ROOM"
|
||||
})
|
||||
break;
|
||||
}
|
||||
case 'joinedrooms':{
|
||||
let data = [
|
||||
...client.rooms
|
||||
].map(e => {
|
||||
return Room.rooms.get(e).toJSON()
|
||||
});
|
||||
|
||||
register('joinedrooms', (client, msg) => {
|
||||
return [...client.rooms].map(e => Room.rooms.get(e).toJSON());
|
||||
});
|
||||
|
||||
register('closeroom', (client, msg) => {
|
||||
const { roomId } = msg;
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: 'fail' };
|
||||
end(data)
|
||||
break;
|
||||
}
|
||||
const room = Room.rooms.get(roomId);
|
||||
if (room.owner === client.id) {
|
||||
case 'closeroom':{
|
||||
let {roomId} = message;
|
||||
if(Room.rooms.has(roomId))
|
||||
{
|
||||
let room = Room.rooms.get(roomId);
|
||||
if(room.owner === client.id)
|
||||
{
|
||||
room.down();
|
||||
return { status: 'success' };
|
||||
}
|
||||
return { status: 'fail' };
|
||||
end({
|
||||
status: 'success'
|
||||
});
|
||||
}else{
|
||||
end({
|
||||
status: 'fail'
|
||||
});
|
||||
|
||||
register('create-room', (client, msg) => {
|
||||
const { error } = CreateRoomValidate.validate(msg);
|
||||
if (error) {
|
||||
return { status: 'fail', messages: error.message };
|
||||
}
|
||||
|
||||
const { name } = msg;
|
||||
for (const [, room] of Room.rooms) {
|
||||
if (name == room.name) {
|
||||
return { status: "fail", message: "ALREADY-EXISTS" };
|
||||
}else{
|
||||
end({
|
||||
status: 'fail'
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'create-room':{
|
||||
let {error} = CreateRoomVerify.validate(message);
|
||||
if(error)
|
||||
{
|
||||
return end({
|
||||
status: 'fail',
|
||||
messages: error.message
|
||||
})
|
||||
}else{
|
||||
let {name} = message;
|
||||
for (const [,{name:RoomName}] of Room.rooms) {
|
||||
if(name == RoomName)
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "ALREADY-EXISTS"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let room = new Room();
|
||||
room.accessType = msg.accessType;
|
||||
room.notifyActionInvite = msg.notifyActionInvite;
|
||||
room.notifyActionJoined = msg.notifyActionJoined;
|
||||
room.notifyActionEjected = msg.notifyActionEjected;
|
||||
room.joinType = msg.joinType;
|
||||
room.description = msg.description;
|
||||
room.name = msg.name;
|
||||
room.accessType = message.accessType;
|
||||
room.notifyActionInvite = message.notifyActionInvite;
|
||||
room.notifyActionJoined = message.notifyActionJoined;
|
||||
room.notifyActionEjected = message.notifyActionEjected;
|
||||
room.joinType = message.joinType;
|
||||
room.description = message.description;
|
||||
room.name = message.name;
|
||||
room.owner = client;
|
||||
|
||||
if (msg.credential) {
|
||||
room.credential = Sha256(msg.credential + "");
|
||||
if(message.credential)
|
||||
{
|
||||
room.credential = Sha256(message.credential + "");
|
||||
}
|
||||
|
||||
room.publish();
|
||||
room.join(client);
|
||||
|
||||
return { status: "success", room: room.toJSON() };
|
||||
end({
|
||||
status: "success",
|
||||
room: room.toJSON()
|
||||
});
|
||||
|
||||
register('joinroom', (client, msg) => {
|
||||
const { name, autoFetchInfo } = msg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'joinroom':{
|
||||
let {name,autoFetchInfo} = message;
|
||||
let roomId;
|
||||
|
||||
for (const [_roomId, room] of Room.rooms) {
|
||||
if (name == room.name) {
|
||||
roomId = _roomId;
|
||||
for (const [_roomId,{name:RoomName}] of Room.rooms) {
|
||||
if(name == RoomName)
|
||||
{
|
||||
roomId = _roomId
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
}
|
||||
|
||||
const room = Room.rooms.get(roomId);
|
||||
|
||||
if (room.joinType == "lock") {
|
||||
return { status: "fail", message: "LOCKED-ROOM" };
|
||||
}
|
||||
|
||||
if (room.joinType == "password") {
|
||||
if (room.credential == Sha256(msg.credential + "")) {
|
||||
let isRoom = Room.rooms.has(roomId);
|
||||
if(isRoom)
|
||||
{
|
||||
let room = Room.rooms.get(roomId);
|
||||
if(room.joinType == "lock")
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "LOCKED-ROOM"
|
||||
})
|
||||
}else if(room.joinType == "password")
|
||||
{
|
||||
if(room.credential == Sha256(message.credential + ""))
|
||||
{
|
||||
let info = {};
|
||||
if (autoFetchInfo) {
|
||||
if(autoFetchInfo)
|
||||
{
|
||||
info.info = room.getInfo();
|
||||
}
|
||||
};
|
||||
room.join(client);
|
||||
return { status: "success", room: room.toJSON(), ...info };
|
||||
}
|
||||
return { status: "fail", message: "WRONG-PASSWORD", area: "credential" };
|
||||
}
|
||||
|
||||
if (room.joinType == "free") {
|
||||
return end({
|
||||
status : "success",
|
||||
room: room.toJSON()
|
||||
})
|
||||
}else return end({
|
||||
status : "fail",
|
||||
message : "WRONG-PASSWORD",
|
||||
area: "credential"
|
||||
})
|
||||
}else if(room.joinType == "free"){
|
||||
let info = {};
|
||||
if (autoFetchInfo) {
|
||||
if(autoFetchInfo)
|
||||
{
|
||||
info.info = room.getInfo();
|
||||
}
|
||||
};
|
||||
room.join(client);
|
||||
return { status: "success", room: room.toJSON(), ...info };
|
||||
}
|
||||
|
||||
if (room.joinType == "invite") {
|
||||
return end({
|
||||
status : "success",
|
||||
room: room.toJSON(),
|
||||
...info
|
||||
})
|
||||
}else if(room.joinType == "invite"){
|
||||
room.waitingInvited.add(client.id);
|
||||
if (room.notifyActionInvite) {
|
||||
room.send([{ id: client.id }, "room/invite"]);
|
||||
if(room.notifyActionInvite)
|
||||
{
|
||||
room.send([{
|
||||
id: client.id
|
||||
},"room/invite"]);
|
||||
}else{
|
||||
room.owner.send([{ id: client.id }, "room/invite"]);
|
||||
room.owner.send([{
|
||||
id: client.id
|
||||
},"room/invite"]);
|
||||
}
|
||||
};
|
||||
}else{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NOT-FOUND-ROOM"
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ejectroom':{
|
||||
let {roomId} = message;
|
||||
let isRoom = Room.rooms.has(roomId);
|
||||
if(isRoom)
|
||||
{
|
||||
let room = Room.rooms.get(roomId);
|
||||
if(room.clients.has(client.id))
|
||||
{
|
||||
room.eject(client)
|
||||
return end({
|
||||
status : "success"
|
||||
})
|
||||
}else{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "ALREADY-ROOM-OUT"
|
||||
})
|
||||
}
|
||||
}else{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NOT-FOUND-ROOM"
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'accept/invite-room':{
|
||||
let {roomId, clientId} = message;
|
||||
// Odanın varlığının kontrolü
|
||||
if(!Room.rooms.has(roomId))
|
||||
{
|
||||
end({
|
||||
status : "fail",
|
||||
message : "NOT-FOUND-ROOM"
|
||||
})
|
||||
};
|
||||
let room = Room.rooms.get(roomId);
|
||||
|
||||
// erişim kontrolü
|
||||
if(!client.rooms.has(room.id))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "FORBIDDEN-INVITE-ACTIONS"
|
||||
})
|
||||
}
|
||||
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
});
|
||||
// Odaya katılma şeklinin doğruluğu
|
||||
if(room.joinType == 'invite')
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "INVALID-DATA"
|
||||
})
|
||||
};
|
||||
// Odaya katılma talebinin doğruluğu
|
||||
if(!room.waitingInvited.includes(clientId))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NO-WAITING-INVITED"
|
||||
})
|
||||
};
|
||||
// Odaya katılan kişinin varlığı
|
||||
if(!Client.clients.has(clientId))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NO-CLIENT"
|
||||
})
|
||||
};
|
||||
|
||||
register('ejectroom', (client, msg) => {
|
||||
const { roomId } = msg;
|
||||
// Odaya kişiyi kabul etme
|
||||
let JoinClient = Client.clients.get(clientId)
|
||||
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
}
|
||||
|
||||
const room = Room.rooms.get(roomId);
|
||||
|
||||
if (!room.clients.has(client.id)) {
|
||||
return { status: "fail", message: "ALREADY-ROOM-OUT" };
|
||||
}
|
||||
|
||||
room.eject(client);
|
||||
return { status: "success" };
|
||||
});
|
||||
|
||||
register('accept/invite-room', (client, msg) => {
|
||||
const { roomId, clientId } = msg;
|
||||
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
}
|
||||
|
||||
const room = Room.rooms.get(roomId);
|
||||
|
||||
if (!client.rooms.has(room.id)) {
|
||||
return { status: "fail", message: "FORBIDDEN-INVITE-ACTIONS" };
|
||||
}
|
||||
|
||||
if (room.joinType == 'invite') {
|
||||
return { status: "fail", message: "INVALID-DATA" };
|
||||
}
|
||||
|
||||
if (!room.waitingInvited.includes(clientId)) {
|
||||
return { status: "fail", message: "NO-WAITING-INVITED" };
|
||||
}
|
||||
|
||||
if (!Client.clients.has(clientId)) {
|
||||
return { status: "fail", message: "NO-CLIENT" };
|
||||
}
|
||||
|
||||
const JoinClient = Client.clients.get(clientId);
|
||||
room.join(JoinClient);
|
||||
JoinClient.send([{ status: "accepted" }, 'room/invite/status']);
|
||||
|
||||
return { status: "success" };
|
||||
JoinClient.send([{
|
||||
status:"accepted"
|
||||
},'room/invite/status']);
|
||||
|
||||
return end({
|
||||
status : "success"
|
||||
});
|
||||
}
|
||||
case 'reject/invite-room':{
|
||||
let {roomId, clientId} = message;
|
||||
// Odanın varlığının kontrolü
|
||||
if(!Room.rooms.has(roomId))
|
||||
{
|
||||
end({
|
||||
status : "fail",
|
||||
message : "NOT-FOUND-ROOM"
|
||||
})
|
||||
};
|
||||
let room = Room.rooms.get(roomId);
|
||||
|
||||
register('reject/invite-room', (client, msg) => {
|
||||
const { roomId, clientId } = msg;
|
||||
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
// erişim kontrolü
|
||||
if(!client.rooms.has(room.id))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "FORBIDDEN-INVITE-ACTIONS"
|
||||
})
|
||||
}
|
||||
|
||||
const room = Room.rooms.get(roomId);
|
||||
// Odaya katılma şeklinin doğruluğu
|
||||
if(room.joinType == 'invite')
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "INVALID-DATA"
|
||||
})
|
||||
};
|
||||
// Odaya katılma talebinin doğruluğu
|
||||
if(!room.waitingInvited.includes(clientId))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NO-WAITING-INVITED"
|
||||
})
|
||||
};
|
||||
// Odaya katılan kişinin varlığı
|
||||
if(!Client.clients.has(clientId))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NO-CLIENT"
|
||||
})
|
||||
};
|
||||
|
||||
if (!client.rooms.has(room.id)) {
|
||||
return { status: "fail", message: "FORBIDDEN-INVITE-ACTIONS" };
|
||||
}
|
||||
// Odaya davet edilen kişiyi reddetme
|
||||
let JoinClient = Client.clients.get(clientId)
|
||||
|
||||
if (room.joinType == 'invite') {
|
||||
return { status: "fail", message: "INVALID-DATA" };
|
||||
}
|
||||
|
||||
if (!room.waitingInvited.includes(clientId)) {
|
||||
return { status: "fail", message: "NO-WAITING-INVITED" };
|
||||
}
|
||||
|
||||
if (!Client.clients.has(clientId)) {
|
||||
return { status: "fail", message: "NO-CLIENT" };
|
||||
}
|
||||
|
||||
const JoinClient = Client.clients.get(clientId);
|
||||
room.waitingInvited = room.waitingInvited.filter(e => e != clientId);
|
||||
room.send([{ id: clientId, roomId: room.id }, 'room/invite/status']);
|
||||
JoinClient.send([{ status: "rejected" }, 'room/invite/status']);
|
||||
|
||||
return { status: "success" };
|
||||
room.send([{id:clientId,roomId:room.id},'room/invite/status'])
|
||||
|
||||
JoinClient.send([{
|
||||
status:"rejected"
|
||||
},'room/invite/status']);
|
||||
|
||||
return end({
|
||||
status : "success"
|
||||
});
|
||||
|
||||
register('room/list', (client, msg) => {
|
||||
const rooms = [];
|
||||
for (const [id, room] of Room.rooms) {
|
||||
if (room.accessType == "public") {
|
||||
}
|
||||
case 'room/list':{
|
||||
let rooms = [];
|
||||
for (const [id, {accessType,name,joinType,description}] of Room.rooms)
|
||||
{
|
||||
if(accessType == "public")
|
||||
{
|
||||
rooms.push({
|
||||
name: room.name,
|
||||
joinType: room.joinType,
|
||||
description: room.description,
|
||||
name,
|
||||
joinType,
|
||||
description,
|
||||
id
|
||||
})
|
||||
}
|
||||
}
|
||||
end({
|
||||
type:'public/rooms',
|
||||
rooms
|
||||
});
|
||||
}
|
||||
case 'room/info':{
|
||||
let {roomId,name} = message;
|
||||
|
||||
// Odanın varlığının kontrolü
|
||||
if(!Room.rooms.has(roomId))
|
||||
{
|
||||
end({
|
||||
status : "fail",
|
||||
message : "NOT-FOUND-ROOM"
|
||||
})
|
||||
};
|
||||
let room = Room.rooms.get(roomId);
|
||||
|
||||
// erişim kontrolü
|
||||
if(!client.rooms.has(room.id))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NO-jıoned-ROOM"
|
||||
})
|
||||
}
|
||||
|
||||
if(name)
|
||||
{
|
||||
return end({
|
||||
status: "success",
|
||||
value: room.info.get(name)
|
||||
});
|
||||
}else{
|
||||
return end({
|
||||
status: "success",
|
||||
value: room.getInfo()
|
||||
});
|
||||
}
|
||||
}
|
||||
return { type: 'public/rooms', rooms };
|
||||
});
|
||||
case 'room/setinfo':{
|
||||
let {roomId,name,value} = message;
|
||||
|
||||
register('room/info', (client, msg) => {
|
||||
const { roomId, name } = msg;
|
||||
// Odanın varlığının kontrolü
|
||||
if(!Room.rooms.has(roomId))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NOT-FOUND-ROOM"
|
||||
})
|
||||
};
|
||||
let room = Room.rooms.get(roomId);
|
||||
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
// erişim kontrolü
|
||||
if(!client.rooms.has(room.id))
|
||||
{
|
||||
return end({
|
||||
status : "fail",
|
||||
message : "NO-JOINED-ROOM"
|
||||
})
|
||||
}
|
||||
|
||||
const room = Room.rooms.get(roomId);
|
||||
|
||||
if (!client.rooms.has(room.id)) {
|
||||
return { status: "fail", message: "NO-JOINED-ROOM" };
|
||||
}
|
||||
|
||||
if (name) {
|
||||
return { status: "success", value: room.info.get(name) };
|
||||
}
|
||||
|
||||
return { status: "success", value: room.getInfo() };
|
||||
});
|
||||
|
||||
register('room/setinfo', (client, msg) => {
|
||||
const { roomId, name, value } = msg;
|
||||
|
||||
if (!Room.rooms.has(roomId)) {
|
||||
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||
}
|
||||
|
||||
const room = Room.rooms.get(roomId);
|
||||
|
||||
if (!client.rooms.has(room.id)) {
|
||||
return { status: "fail", message: "NO-JOINED-ROOM" };
|
||||
}
|
||||
|
||||
room.info.set(name, value);
|
||||
|
||||
room.send(
|
||||
[{ name, value, roomId: room.id }, "room/info"],
|
||||
[
|
||||
{
|
||||
name,
|
||||
value,
|
||||
roomId:room.id
|
||||
},
|
||||
"room/info"
|
||||
],
|
||||
client.id,
|
||||
c => c.roomInfoNotifiable()
|
||||
client => {
|
||||
return client.roomInfoNotifiable()
|
||||
}
|
||||
);
|
||||
|
||||
return { status: "success" };
|
||||
return end({
|
||||
status: "success"
|
||||
});
|
||||
}
|
||||
default:{
|
||||
next();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
exports.Room = Room;
|
||||
|
|
@ -1,43 +1,69 @@
|
|||
"use strict";
|
||||
let {addService,addListener} = require("../WebSocket.js");
|
||||
|
||||
const { on, emit, register } = require("../WebSocket");
|
||||
const Stdoutput = [
|
||||
["notifyPairInfo", true],
|
||||
["packrecaive", true],
|
||||
["packsending", true],
|
||||
["notifyRoomInfo", true]
|
||||
];
|
||||
|
||||
const defaults = {
|
||||
notifyPairInfo: true,
|
||||
packrecaive: true,
|
||||
packsending: true,
|
||||
notifyRoomInfo: true
|
||||
};
|
||||
addListener('connect',(global, client)=>{
|
||||
for (const [name, defaultValue] of Stdoutput)
|
||||
{
|
||||
client.store.set(name, defaultValue);
|
||||
}
|
||||
client.sync('store');
|
||||
});
|
||||
|
||||
on('connect', (client) => {
|
||||
for (const [name, value] of Object.entries(defaults)) {
|
||||
client.store.set(name, value);
|
||||
|
||||
|
||||
addService(({
|
||||
client,
|
||||
end,
|
||||
global,
|
||||
message,
|
||||
next,
|
||||
response
|
||||
})=>{
|
||||
let {type, value} = message;
|
||||
switch(type)
|
||||
{
|
||||
// enable/disable pair/info messages
|
||||
case 'connection/pairinfo':{
|
||||
client.store.set("notifyPairInfo", !!value)
|
||||
client.sync('store');
|
||||
end(true);
|
||||
break;
|
||||
}
|
||||
case 'connection/roominfo':{
|
||||
client.store.set("notifyRoomInfo", !!value)
|
||||
client.sync('store');
|
||||
end(true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 'connection/packrecaive':{
|
||||
client.store.set("packrecaive", !!value)
|
||||
client.sync('store');
|
||||
end(true);
|
||||
break;
|
||||
}
|
||||
case 'connection/packsending':{
|
||||
client.store.set("packsending", !!value)
|
||||
client.sync('store');
|
||||
end(true);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'connection/reset':{
|
||||
for (const [name, defaultValue] of Stdoutput)
|
||||
{
|
||||
client.store.set(name, defaultValue);
|
||||
}
|
||||
client.sync('store');
|
||||
end(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
register('connection/pairinfo', (client, msg) => {
|
||||
client.store.set("notifyPairInfo", !!msg.value);
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('connection/roominfo', (client, msg) => {
|
||||
client.store.set("notifyRoomInfo", !!msg.value);
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('connection/packrecaive', (client, msg) => {
|
||||
client.store.set("packrecaive", !!msg.value);
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('connection/packsending', (client, msg) => {
|
||||
client.store.set("packsending", !!msg.value);
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
||||
register('connection/reset', (client, msg) => {
|
||||
for (const [name, value] of Object.entries(defaults)) {
|
||||
client.store.set(name, value);
|
||||
}
|
||||
return { status: 'success' };
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
"use strict";
|
||||
let {addListener} = require("../WebSocket.js");
|
||||
|
||||
const { on } = require("../WebSocket");
|
||||
|
||||
on('connect', (client) => {
|
||||
client.send([{ type: 'id', value: client.id }, 'id']);
|
||||
addListener('connect',(global, client)=>{
|
||||
client.send([{
|
||||
type: 'id',
|
||||
value: client.id
|
||||
},'id'])
|
||||
});
|
||||
|
|
@ -1,94 +1,170 @@
|
|||
"use strict";
|
||||
|
||||
let websocket = require("websocket");
|
||||
let http = null;
|
||||
let wsServer = null;
|
||||
let {http} = require("./HTTPServer");
|
||||
let {randomUUID} = require("crypto");
|
||||
const { Client } = require("./Client.js");
|
||||
const { termoutput } = require("./config");
|
||||
const EventEmitter = require("./EventEmitter");
|
||||
const MessageRouter = require("./MessageRouter");
|
||||
|
||||
function init(server) {
|
||||
http = server;
|
||||
|
||||
const { CLIENT_CREATED, CLIENT_DESTROY } = require("./IPC");
|
||||
const stats = require("./stats");
|
||||
termoutput && console.log("Web Socket Protocol is ready");
|
||||
|
||||
http.addListener("upgrade",() => {
|
||||
termoutput && console.log("HTTP Upgrading to WebSocket");
|
||||
});
|
||||
})
|
||||
|
||||
wsServer = new websocket.server({
|
||||
let wsServer = new websocket.server({
|
||||
httpServer: http,
|
||||
autoAcceptConnections: true
|
||||
});
|
||||
/*
|
||||
process.send({
|
||||
core: "writestat",
|
||||
writeBytes:0,
|
||||
readBytes:0,
|
||||
totalBytes:0,
|
||||
recaivedPacket:0,
|
||||
sendedPacket:0,
|
||||
totalPacket:0
|
||||
})*/
|
||||
|
||||
let global = new Map();
|
||||
let clients = new Map();
|
||||
|
||||
|
||||
wsServer.addListener("connect",(socket) => {
|
||||
let client = new Client();
|
||||
let xClient = new Client();
|
||||
let id = randomUUID();
|
||||
socket.id = id;
|
||||
client.id = id;
|
||||
client.socket = socket;
|
||||
client.created_at = new Date();
|
||||
Client.clients.set(id, client);
|
||||
xClient.id = id;
|
||||
xClient.socket = socket;
|
||||
xClient.created_at = new Date();
|
||||
Client.clients.set(id, xClient);
|
||||
clients.set(id, xClient);
|
||||
|
||||
EventEmitter.emit('connect', client);
|
||||
stats.mwse_clients++;
|
||||
|
||||
CLIENT_CREATED(id);
|
||||
|
||||
emit("connect", global, xClient);
|
||||
|
||||
let oldw = 0, oldr = 0;
|
||||
let timer = setInterval(()=>{
|
||||
let writed = socket.socket.bytesRead - oldr;
|
||||
let readed = socket.socket.bytesWritten - oldw;
|
||||
stats.ws_total_bytes += (writed + readed);
|
||||
stats.ws_readed_bytes += readed;
|
||||
stats.ws_writed_bytes += writed;
|
||||
oldr = socket.socket.bytesRead;
|
||||
oldw = socket.socket.bytesWritten;
|
||||
}, 1000)
|
||||
|
||||
socket.addListener("close",()=>{
|
||||
stats.mwse_clients--;
|
||||
emit("disconnect", global, xClient);
|
||||
CLIENT_DESTROY(id);
|
||||
Client.clients.delete(id);
|
||||
clearInterval(timer);
|
||||
clearInterval(pingTimer);
|
||||
});
|
||||
|
||||
let pingTimer = setInterval(()=> socket.ping('saQut') , 10_000);
|
||||
|
||||
socket.addListener("pong", (validationText) => {
|
||||
socket.addListener("pong",validationText => {
|
||||
if(validationText.toString('utf8') != "saQut"){
|
||||
socket.close();
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
socket.addListener("message",({type,utf8Data}) => {
|
||||
if (type == "utf8") {
|
||||
stats.ws_recaived_packs++;
|
||||
stats.ws_total_packs++;
|
||||
|
||||
if(type == "utf8")
|
||||
{
|
||||
let json;
|
||||
try{
|
||||
const json = JSON.parse(utf8Data);
|
||||
const [message, id, action] = json;
|
||||
|
||||
let response;
|
||||
|
||||
if (typeof id === 'number' || typeof id === 'string') {
|
||||
response = MessageRouter.handle(client, message);
|
||||
|
||||
if (action === 'R') {
|
||||
client.send([response, id, 'E']);
|
||||
} else if (action === 'S') {
|
||||
client.send([response, id, 'C']);
|
||||
}
|
||||
} else {
|
||||
const result = MessageRouter.handle(client, message);
|
||||
|
||||
if (result && result.broadcast) {
|
||||
EventEmitter.emit('broadcast', result.broadcast, client);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
EventEmitter.emit('messageError', client, utf8Data);
|
||||
json = JSON.parse(utf8Data);
|
||||
emit('services', global, xClient, json);
|
||||
let [payload, id, action] = json;
|
||||
if(typeof id === "string")
|
||||
{
|
||||
action = id;
|
||||
id = void 0;
|
||||
};
|
||||
emitService(global, xClient, id, payload, action);
|
||||
}catch{
|
||||
emit("messageError", global, xClient, utf8Data);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.addListener("close", () => {
|
||||
EventEmitter.emit('disconnect', client);
|
||||
Client.clients.delete(id);
|
||||
clearInterval(pingTimer);
|
||||
});
|
||||
});
|
||||
/**
|
||||
* @type {Map<string, Function[]>}
|
||||
*/
|
||||
let events = new Map();
|
||||
/**
|
||||
* @type {Map<string, Function[]>}
|
||||
*/
|
||||
let services = []
|
||||
/**
|
||||
*
|
||||
* @param {string} event
|
||||
* @param {(global:Map<string, any>, client:Client, data:any) => any} func
|
||||
*/
|
||||
exports.addListener = (event, func) => {
|
||||
if(!events.has(event))
|
||||
{
|
||||
events.set(event,[]);
|
||||
};
|
||||
events.get(event).push(func);
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param {string} event
|
||||
* @param {(data:{global:Map<string, any>, client:Client, message:any,response:Function,end:Function,next:Function}) => any} func
|
||||
*/
|
||||
exports.addService = (func) => {
|
||||
services.push(func);
|
||||
};
|
||||
function emit(event,...args)
|
||||
{
|
||||
if(events.has(event))
|
||||
{
|
||||
for (const callback of events.get(event)) {
|
||||
callback(...args);
|
||||
}
|
||||
|
||||
const on = EventEmitter.on;
|
||||
const emit = EventEmitter.emit;
|
||||
const off = EventEmitter.off;
|
||||
|
||||
const register = MessageRouter.register;
|
||||
const handle = MessageRouter.handle;
|
||||
|
||||
exports.init = init;
|
||||
exports.on = on;
|
||||
exports.emit = emit;
|
||||
exports.off = off;
|
||||
exports.register = register;
|
||||
exports.handle = handle;
|
||||
};
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param {Map} global
|
||||
* @param {Client} local
|
||||
* @param {number} id
|
||||
* @param {{[key:string]:any}} payload
|
||||
* @param {"R"|"S"} action [R]EQUEST flag or [S]TREAM flag
|
||||
*/
|
||||
async function emitService(global, client, id, payload, action)
|
||||
{
|
||||
let willContinue = false;
|
||||
for (const callback of services) {
|
||||
await callback({
|
||||
message: payload,
|
||||
action,
|
||||
client,
|
||||
global,
|
||||
messageId: id,
|
||||
response:(obj)=>{
|
||||
id != undefined && client.send([obj, id, 'C']) // continue ([C]ONTINUE flag)
|
||||
},
|
||||
end:(obj)=>{
|
||||
id != undefined && client.send([obj, id, 'E']) // stopped data stream (this channel) ([E]ND flag)
|
||||
},
|
||||
next:function(){
|
||||
willContinue = true;
|
||||
}
|
||||
});
|
||||
if(willContinue === false) break;
|
||||
}
|
||||
};
|
||||
exports.websocket = wsServer;
|
||||
235
Source/api.js
235
Source/api.js
|
|
@ -1,235 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const { Client } = require("./Client");
|
||||
const { Room } = require("./Services/Room");
|
||||
|
||||
const apiKeys = new Map();
|
||||
const webhooks = new Map();
|
||||
|
||||
function auth(req, res, next) {
|
||||
const key = req.headers['x-api-key'];
|
||||
|
||||
if (!key) {
|
||||
return res.status(401).json({ status: 'fail', message: 'API_KEY_REQUIRED' });
|
||||
}
|
||||
|
||||
if (!apiKeys.has(key)) {
|
||||
return res.status(401).json({ status: 'fail', message: 'INVALID_API_KEY' });
|
||||
}
|
||||
|
||||
req.server = apiKeys.get(key);
|
||||
next();
|
||||
}
|
||||
|
||||
router.post('/auth/key', (req, res) => {
|
||||
const { domain } = req.body;
|
||||
|
||||
if (!domain) {
|
||||
return res.json({ status: 'fail', message: 'DOMAIN_REQUIRED' });
|
||||
}
|
||||
|
||||
const key = require("crypto").randomUUID();
|
||||
apiKeys.set(key, { domain, key });
|
||||
|
||||
res.json({ status: 'success', key });
|
||||
});
|
||||
|
||||
router.post('/client/:id/send', auth, (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { pack } = req.body;
|
||||
|
||||
const client = Client.clients.get(id);
|
||||
|
||||
if (!client) {
|
||||
return res.json({ status: 'fail', message: 'CLIENT_NOT_FOUND' });
|
||||
}
|
||||
|
||||
if (!pack) {
|
||||
return res.json({ status: 'fail', message: 'PACK_REQUIRED' });
|
||||
}
|
||||
|
||||
const fromServer = req.server;
|
||||
client.send([{ from: 'server', fromServer, pack }, 'server/pack']);
|
||||
|
||||
res.json({ status: 'success' });
|
||||
});
|
||||
|
||||
router.post('/room/:id/send', auth, (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { pack, wom } = req.body;
|
||||
|
||||
const room = Room.rooms.get(id);
|
||||
|
||||
if (!room) {
|
||||
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||
}
|
||||
|
||||
if (!pack) {
|
||||
return res.json({ status: 'fail', message: 'PACK_REQUIRED' });
|
||||
}
|
||||
|
||||
const fromServer = req.server;
|
||||
|
||||
room.send(
|
||||
[{ from: 'server', fromServer, pack, roomId: id }, 'server/pack/room'],
|
||||
wom ? undefined : 'server',
|
||||
() => true
|
||||
);
|
||||
|
||||
res.json({ status: 'success' });
|
||||
});
|
||||
|
||||
router.post('/room/create', auth, (req, res) => {
|
||||
const { name, accessType, joinType, description, credential } = req.body;
|
||||
|
||||
if (!name) {
|
||||
return res.json({ status: 'fail', message: 'NAME_REQUIRED' });
|
||||
}
|
||||
|
||||
for (const [, room] of Room.rooms) {
|
||||
if (room.name === name) {
|
||||
return res.json({ status: 'fail', message: 'ROOM_ALREADY_EXISTS' });
|
||||
}
|
||||
}
|
||||
|
||||
const room = new Room();
|
||||
room.name = name;
|
||||
room.accessType = accessType || 'public';
|
||||
room.joinType = joinType || 'free';
|
||||
room.description = description || '';
|
||||
room.owner = { id: 'server', isServer: true };
|
||||
|
||||
if (credential) {
|
||||
const { createHash } = require("crypto");
|
||||
room.credential = createHash("sha256").update(credential).digest("hex");
|
||||
}
|
||||
|
||||
room.publish();
|
||||
|
||||
res.json({ status: 'success', room: room.toJSON() });
|
||||
});
|
||||
|
||||
router.post('/room/:id/join', auth, (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { credential } = req.body;
|
||||
|
||||
const room = Room.rooms.get(id);
|
||||
|
||||
if (!room) {
|
||||
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||
}
|
||||
|
||||
if (room.joinType === 'lock') {
|
||||
return res.json({ status: 'fail', message: 'ROOM_LOCKED' });
|
||||
}
|
||||
|
||||
if (room.joinType === 'password') {
|
||||
const { createHash } = require("crypto");
|
||||
const hash = createHash(credential || '').digest("hex");
|
||||
if (room.credential !== hash) {
|
||||
return res.json({ status: 'fail', message: 'WRONG_PASSWORD' });
|
||||
}
|
||||
}
|
||||
|
||||
const fakeClient = {
|
||||
id: 'server-joined',
|
||||
isServer: true,
|
||||
rooms: new Set(),
|
||||
send: (msg) => {
|
||||
const fromServer = req.server;
|
||||
room.send([{ from: 'server', fromServer, ...msg[0] }, msg[1]], 'server');
|
||||
}
|
||||
};
|
||||
|
||||
room.join(fakeClient);
|
||||
|
||||
res.json({ status: 'success', room: room.toJSON() });
|
||||
});
|
||||
|
||||
router.delete('/room/:id/leave', auth, (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
const room = Room.rooms.get(id);
|
||||
|
||||
if (!room) {
|
||||
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||
}
|
||||
|
||||
const fakeClient = { id: 'server-joined', isServer: true };
|
||||
room.eject(fakeClient);
|
||||
|
||||
res.json({ status: 'success' });
|
||||
});
|
||||
|
||||
router.get('/room/:id', (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
const room = Room.rooms.get(id);
|
||||
|
||||
if (!room) {
|
||||
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||
}
|
||||
|
||||
res.json({ status: 'success', room: room.toJSON() });
|
||||
});
|
||||
|
||||
router.get('/rooms', (req, res) => {
|
||||
const rooms = [];
|
||||
|
||||
for (const [id, room] of Room.rooms) {
|
||||
rooms.push({
|
||||
id,
|
||||
name: room.name,
|
||||
accessType: room.accessType,
|
||||
joinType: room.joinType,
|
||||
description: room.description,
|
||||
clientCount: room.clients.size
|
||||
});
|
||||
}
|
||||
|
||||
res.json({ status: 'success', rooms });
|
||||
});
|
||||
|
||||
router.get('/clients', (req, res) => {
|
||||
const clients = [];
|
||||
|
||||
for (const [id, client] of Client.clients) {
|
||||
clients.push({
|
||||
id,
|
||||
rooms: [...client.rooms],
|
||||
pairs: [...client.pairs]
|
||||
});
|
||||
}
|
||||
|
||||
res.json({ status: 'success', clients });
|
||||
});
|
||||
|
||||
router.post('/webhook', auth, (req, res) => {
|
||||
const { url, events } = req.body;
|
||||
|
||||
if (!url) {
|
||||
return res.json({ status: 'fail', message: 'URL_REQUIRED' });
|
||||
}
|
||||
|
||||
const server = req.server;
|
||||
webhooks.set(server.domain, { url, events: events || ['server/pack', 'server/pack/room'] });
|
||||
|
||||
res.json({ status: 'success' });
|
||||
});
|
||||
|
||||
function triggerWebhook(event, data) {
|
||||
for (const [, webhook] of webhooks) {
|
||||
if (webhook.events.includes(event)) {
|
||||
fetch(webhook.url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ event, data })
|
||||
}).catch(console.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
module.exports.triggerWebhook = triggerWebhook;
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
require("./HTTPServer.js");
|
||||
|
||||
const { http } = require("./HTTPServer");
|
||||
|
||||
const WebSocket = require("./WebSocket");
|
||||
WebSocket.init(http);
|
||||
require("./WebSocket.js");
|
||||
|
||||
require("./Services/YourID.js");
|
||||
require("./Services/Auth.js");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
const { mlog } = require("./IPC");
|
||||
|
||||
exports.ws_writed_bytes = 0;
|
||||
exports.ws_readed_bytes = 0;
|
||||
exports.ws_total_bytes = 0;
|
||||
exports.ws_sended_packs = 0;
|
||||
exports.ws_recaived_packs = 0;
|
||||
exports.ws_total_packs = 0;
|
||||
exports.mwse_rooms = 0;
|
||||
exports.mwse_clients = 0;
|
||||
|
||||
|
||||
process.send({
|
||||
core: "writestat",
|
||||
ws_writed_bytes: exports.ws_writed_bytes,
|
||||
ws_readed_bytes: exports.ws_readed_bytes,
|
||||
ws_total_bytes: exports.ws_total_bytes,
|
||||
ws_sended_packs: exports.ws_sended_packs,
|
||||
ws_recaived_packs: exports.ws_recaived_packs,
|
||||
ws_total_packs: exports.ws_total_packs,
|
||||
mwse_rooms: exports.mwse_rooms,
|
||||
mwse_clients: exports.mwse_clients
|
||||
});
|
||||
setInterval(()=>{
|
||||
process.send({
|
||||
core: "writestat",
|
||||
ws_writed_bytes: exports.ws_writed_bytes,
|
||||
ws_readed_bytes: exports.ws_readed_bytes,
|
||||
ws_total_bytes: exports.ws_total_bytes,
|
||||
ws_sended_packs: exports.ws_sended_packs,
|
||||
ws_recaived_packs: exports.ws_recaived_packs,
|
||||
ws_total_packs: exports.ws_total_packs,
|
||||
mwse_rooms: exports.mwse_rooms,
|
||||
mwse_clients: exports.mwse_clients
|
||||
})
|
||||
mlog(`writed ${exports.ws_writed_bytes} bytes, readed ${exports.ws_readed_bytes} bytes`);
|
||||
exports.ws_writed_bytes = 0;
|
||||
exports.ws_readed_bytes = 0;
|
||||
exports.ws_total_bytes = 0;
|
||||
exports.ws_sended_packs = 0;
|
||||
exports.ws_recaived_packs = 0;
|
||||
exports.ws_total_packs = 0;
|
||||
process.send({
|
||||
core: "readstat",
|
||||
ws_writed_bytes: exports.ws_writed_bytes,
|
||||
ws_readed_bytes: exports.ws_readed_bytes,
|
||||
ws_total_bytes: exports.ws_total_bytes,
|
||||
ws_sended_packs: exports.ws_sended_packs,
|
||||
ws_recaived_packs: exports.ws_recaived_packs,
|
||||
ws_total_packs: exports.ws_total_packs,
|
||||
mwse_rooms: exports.mwse_rooms,
|
||||
mwse_clients: exports.mwse_clients
|
||||
})
|
||||
}, 3000)
|
||||
|
||||
process.on('message', stat => {
|
||||
if(stat.type == ':stats:')
|
||||
{
|
||||
exports.others = stat.data;
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,508 @@
|
|||
function Hemex()
|
||||
{
|
||||
|
||||
};
|
||||
Hemex.EOL = "\n";
|
||||
/**
|
||||
* Hemex variable white space chars
|
||||
* @type {Number[]}
|
||||
*/
|
||||
Hemex.WhiteSpace = [
|
||||
9,10,11,12,13,32,133
|
||||
];
|
||||
/**
|
||||
* Current cursor position
|
||||
* @type {Number}
|
||||
*/
|
||||
Hemex.prototype.offset = 0;
|
||||
/**
|
||||
* Mapping offset points
|
||||
* @type {Number[]}
|
||||
*/
|
||||
Hemex.prototype.offsetMap = [];
|
||||
Hemex.prototype.beginPosition = function(){
|
||||
this.offsetMap.push(
|
||||
this.getLastPosition()
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Adding current position to offset map
|
||||
*/
|
||||
Hemex.prototype.acceptPosition = function(){
|
||||
let t = this.offsetMap.pop();
|
||||
this.setLastPosition(t)
|
||||
}
|
||||
/**
|
||||
* Get text range current and parent offsets
|
||||
* @returns {[Number,Number]}
|
||||
*/
|
||||
Hemex.prototype.positionRange = function(){
|
||||
let len = this.offsetMap.length;
|
||||
if(len == 0)
|
||||
{
|
||||
return [0,this.offset]
|
||||
}else if(len == 1){
|
||||
return [this.offset,this.offsetMap[len - 1]]
|
||||
}else{
|
||||
return [this.offsetMap[len - 2],this.offsetMap[len - 1]]
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get text range between current offset and parent offset
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.getPositionRange = function(){
|
||||
let u = this.positionRange();
|
||||
return this.text.slice(u[0],u[1])
|
||||
}
|
||||
/**
|
||||
* Cancel current position and return to parent offset
|
||||
*/
|
||||
Hemex.prototype.rejectPosition = function(){
|
||||
this.offsetMap.pop()
|
||||
}
|
||||
/**
|
||||
* Get current layer of position from last offset of map
|
||||
* @returns {Number}
|
||||
*/
|
||||
Hemex.prototype.getLastPosition = function(){
|
||||
return this.offsetMap.length == 0 ? this.offset : this.offsetMap.slice(-1)[0]
|
||||
}
|
||||
/**
|
||||
* Set last position offset from offset map last layer
|
||||
* @param {Number} n
|
||||
*/
|
||||
Hemex.prototype.setLastPosition = function(n){
|
||||
if(this.offsetMap.length == 0)
|
||||
this.offset = n
|
||||
else this.offsetMap[this.offsetMap.length - 1] = n
|
||||
}
|
||||
/**
|
||||
* Get current layer of position from last offset of map
|
||||
* Some as getLastPosition()
|
||||
* @returns {Number}
|
||||
*/
|
||||
Hemex.prototype.getOffset = function(){
|
||||
return this.getLastPosition()
|
||||
}
|
||||
/**
|
||||
* Set last position offset from offset map last layer and return it value
|
||||
* @param {Number} n
|
||||
* @returns {Number}
|
||||
*/
|
||||
Hemex.prototype.setOffset = function(n){
|
||||
this.setLastPosition(n);
|
||||
return this.getLastPosition()
|
||||
}
|
||||
/**
|
||||
* Get text length
|
||||
* @type {Number}
|
||||
*/
|
||||
Hemex.prototype.length = 0;
|
||||
|
||||
/**
|
||||
* Hemex lexing data
|
||||
* @type {String}
|
||||
*/
|
||||
Hemex.prototype.text = "";
|
||||
|
||||
/**
|
||||
* set lexing data
|
||||
* @param {String} text
|
||||
* @returns {void}
|
||||
*/
|
||||
Hemex.prototype.setText = function(text){
|
||||
this.offset = 0;
|
||||
this.length = text.length;
|
||||
this.offsetMap = [];
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* get lexing all data
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.getText = function(){
|
||||
return this.text;
|
||||
}
|
||||
/**
|
||||
* Get one character from cursor
|
||||
* @param {Number} n
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.getChar = function(n){
|
||||
return this.text.charAt(n?this.getOffset()+n:this.getOffset())
|
||||
}
|
||||
/**
|
||||
* Boolean
|
||||
* @param {Number} n
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.isChar = function(b){
|
||||
return this.getChar() == b
|
||||
}
|
||||
/**
|
||||
* Dump all data from cursor position to end of char
|
||||
* @param {Number} n
|
||||
*/
|
||||
Hemex.prototype.dump = function(n){
|
||||
return this.text.slice(this.getOffset(),this.getOffset()+n)
|
||||
}
|
||||
/**
|
||||
* Control coming end of line
|
||||
* @returns {Bollean}
|
||||
*/
|
||||
Hemex.prototype.isEnd = function(){
|
||||
return this.length > this.getOffset()
|
||||
}
|
||||
/**
|
||||
* Forward one char
|
||||
*/
|
||||
Hemex.prototype.nextChar = function(){
|
||||
this.setOffset(this.getOffset() + 1);
|
||||
}
|
||||
/**
|
||||
* Forward n char
|
||||
*/
|
||||
Hemex.prototype.toChar = function(n){
|
||||
this.setOffset(this.getOffset() + n);
|
||||
}
|
||||
/**
|
||||
* Reading while end of line
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.getLine = function(){
|
||||
return this.of(function(){
|
||||
switch(this.getChar())
|
||||
{
|
||||
case Hemex.EOL: return false;
|
||||
default: return true;
|
||||
}
|
||||
}.bind(this))
|
||||
}
|
||||
/**
|
||||
* Read all data until the function returns false
|
||||
* @param {Boolean} p
|
||||
* @param {(char:String)=>Boolean} e
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.of = function(e,p){
|
||||
let k = [],count=0;
|
||||
while(this.isEnd()){
|
||||
if(e(p,count)) k.push(this.getChar());
|
||||
else return k.join('');
|
||||
count++;
|
||||
this.nextChar();
|
||||
};
|
||||
return k.length == 0 ? false : k.join('')
|
||||
}
|
||||
Hemex.prototype.each = function(e,p){
|
||||
let k = [];
|
||||
while(this.isEnd())
|
||||
if(!e(p)) return;
|
||||
else this.nextChar();
|
||||
}
|
||||
Hemex.prototype.while = function(e,p){
|
||||
let k = [];
|
||||
while(this.isEnd())
|
||||
if(!e(p)) return;
|
||||
}
|
||||
/**
|
||||
* Controlling for current char type
|
||||
* @param {Boolean} reverse
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Hemex.prototype.isNumber = function(reverse){
|
||||
let c = this.getChar().charCodeAt(0);
|
||||
let result = c >= 48 && c <= 57;
|
||||
return reverse ? !result : result;
|
||||
}
|
||||
/**
|
||||
* Read all data until char type is not number
|
||||
* @param {Boolean} reverse
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.readNumbers = function(reverse){
|
||||
return this.of(this.isNumber.bind(this),reverse)
|
||||
}
|
||||
/**
|
||||
* Controlling for current char type
|
||||
* @param {Boolean} reverse
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Hemex.prototype.isBigLetter = function(reverse){
|
||||
let c = this.getChar().charCodeAt(0);
|
||||
let result = c >= 97 && c <= 122;
|
||||
return reverse ? !result : result;
|
||||
}
|
||||
/**
|
||||
* Controlling for current char type
|
||||
* @param {Boolean} reverse
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Hemex.prototype.isSmallLetter = function(reverse){
|
||||
let c = this.getChar().charCodeAt(0);
|
||||
let result = c >= 65 && c <= 90;
|
||||
return reverse ? !result : result;
|
||||
}
|
||||
/**
|
||||
* Controlling for current char type
|
||||
* @param {Boolean} reverse
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Hemex.prototype.isLetter = function(reverse){
|
||||
let result = this.isSmallLetter() || this.isBigLetter()
|
||||
return reverse ? !result : result;
|
||||
}
|
||||
/**
|
||||
* Read all data until char type is not letter
|
||||
* @param {Boolean} reverse
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.readLetters = function(reverse){
|
||||
return this.of(this.isLetter.bind(this),reverse)
|
||||
}
|
||||
/**
|
||||
* Controlling for current char type
|
||||
* @param {Boolean} reverse
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Hemex.prototype.isWhiteSpace = function(reverse){
|
||||
let c = this.getChar(),ct = c.charCodeAt(0);
|
||||
let result = (
|
||||
c == '\n' ||
|
||||
c == '\r' ||
|
||||
c == '\t' ||
|
||||
c == ' ' ||
|
||||
Hemex.WhiteSpace.includes(ct)
|
||||
)
|
||||
return reverse ? !result : result;
|
||||
}
|
||||
/**
|
||||
* Read all data until char type is not white space
|
||||
* @param {Boolean} reverse
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.readWhiteSpace = function(reverse){
|
||||
return this.of(this.isWhiteSpace.bind(this),reverse)
|
||||
}
|
||||
/**
|
||||
* Controlling data
|
||||
* @param {Boolean} reverse
|
||||
* @returns {String}
|
||||
*/
|
||||
Hemex.prototype.include = function(words,next){
|
||||
this.beginPosition();
|
||||
for(let i = 0; i<words.length; i++)
|
||||
{
|
||||
if(words[i] != this.getChar())
|
||||
{
|
||||
this.rejectPosition();
|
||||
return false;
|
||||
};
|
||||
this.nextChar();
|
||||
};
|
||||
if(next) this.acceptPosition();
|
||||
else this.rejectPosition();
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Controlling data
|
||||
* @param {Boolean} reverse
|
||||
* @returns {String|boolean}
|
||||
*/
|
||||
Hemex.prototype.includes = function(arrays,accept){
|
||||
this.beginPosition();
|
||||
let flags = Array.from(arrays).fill(true);
|
||||
let index = 0;
|
||||
this.each(function(){
|
||||
let stopLoop = true;
|
||||
for(let T in arrays)
|
||||
{
|
||||
if(!flags[T] || arrays[T].length <= index) continue;
|
||||
stopLoop = false;
|
||||
flags[T] &= arrays[T][index] == this.getChar()
|
||||
};
|
||||
index++;
|
||||
return !stopLoop && flags.filter(function(val){return val}).length != 0;
|
||||
}.bind(this));
|
||||
let result = arrays.filter(function(_,index){return flags[index]});
|
||||
if(accept) this.acceptPosition();
|
||||
else this.rejectPosition();
|
||||
return result.length == 0 ? false : result
|
||||
}
|
||||
/**
|
||||
* Parsing number formats like; 12 75.1 0xE7 0b10 +3.46
|
||||
* @returns {[String,Number]}
|
||||
*/
|
||||
Hemex.prototype.readNumber = function(){
|
||||
let data = [];
|
||||
let base = 10;
|
||||
let nextDot = false;
|
||||
let c = this.getChar();
|
||||
if(this.isChar('0'))
|
||||
{
|
||||
this.nextChar();
|
||||
switch(this.getChar())
|
||||
{
|
||||
case 'x':{
|
||||
base = 16;
|
||||
this.nextChar();
|
||||
data.push('0x')
|
||||
break;
|
||||
}
|
||||
case 'b':{
|
||||
base = 2;
|
||||
this.nextChar();
|
||||
data.push('0b')
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
base = 8;
|
||||
this.nextChar();
|
||||
data.push('0')
|
||||
}
|
||||
}
|
||||
}else base = 10;
|
||||
this.each(()=>{
|
||||
switch(c = this.getChar())
|
||||
{
|
||||
case '0':
|
||||
case '1':{
|
||||
data.push(c);
|
||||
break;
|
||||
}
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':{
|
||||
if(base >= 8){
|
||||
data.push(c);
|
||||
break;
|
||||
}else return false;
|
||||
}
|
||||
case '8':
|
||||
case '9':{
|
||||
if(base >= 10){
|
||||
data.push(c);
|
||||
break;
|
||||
}else return false;
|
||||
}
|
||||
case 'A':
|
||||
case 'a':
|
||||
case 'B':
|
||||
case 'b':
|
||||
case 'C':
|
||||
case 'c':
|
||||
case 'D':
|
||||
case 'd':
|
||||
/* case 'E': case 'e': */
|
||||
case 'F':
|
||||
case 'f':{
|
||||
if(base >= 16){
|
||||
data.push(c);
|
||||
break;
|
||||
}else return false;
|
||||
}
|
||||
case '.':{
|
||||
if(!nextDot){
|
||||
if(data.length == 0){
|
||||
data.push("0");
|
||||
}else data.push(".");
|
||||
nextDot = true;
|
||||
isFloat = true;
|
||||
}else{
|
||||
throw new Error("Float number in Double dot");
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'E':
|
||||
case 'e':{
|
||||
if(this.getChar(1)!='+'){
|
||||
if(base == 16){
|
||||
data.push(c);
|
||||
break;
|
||||
}else return false;
|
||||
};
|
||||
if(data.length == 0){
|
||||
this.rejectPosition();
|
||||
return false;
|
||||
};
|
||||
data.push('e');
|
||||
this.nextChar();
|
||||
if(this.getChar()=='+' || this.getChar()=='-'){
|
||||
data.push(char());
|
||||
this.nextChar();
|
||||
};
|
||||
let result = null;
|
||||
this.each(()=>{
|
||||
switch(this.getChar()){
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':{
|
||||
data.push(this.getChar());
|
||||
this.nextChar();
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
default:{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return true
|
||||
});
|
||||
return data.length == 0 ? false : [data.join(''),base]
|
||||
}
|
||||
|
||||
Hemex.prototype.syntaxs = new Map();
|
||||
/**
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {(hmx:Hemex, result: (result:any) => any,...args:any[]) => any} callback
|
||||
*/
|
||||
Hemex.prototype.syntax = function(name, callback){
|
||||
this.syntaxs.set(name, callback)
|
||||
}
|
||||
|
||||
Hemex.prototype.give = function(name, ...args){
|
||||
let sandbox = this.syntaxs.get(name);
|
||||
if(sandbox)
|
||||
{
|
||||
let res = undefined;
|
||||
hmx.beginPosition();
|
||||
if(sandbox(
|
||||
this,
|
||||
arg => {
|
||||
res = arg
|
||||
},
|
||||
...args
|
||||
))
|
||||
{
|
||||
hmx.acceptPosition();
|
||||
return res;
|
||||
}else{
|
||||
hmx.rejectPosition();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {Error} message
|
||||
*/
|
||||
Hemex.prototype.throw = function(message){
|
||||
throw new Error(message)
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="tr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<base href="/console/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebSocket Terminal</title>
|
||||
<style>
|
||||
body{
|
||||
background-color: black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="terminal"></div>
|
||||
<script src="xterm.js"></script>
|
||||
<link rel="stylesheet" href="xterm.min.css">
|
||||
<script src="./Hemex.js"></script>
|
||||
<script src="lib.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,920 @@
|
|||
var term = new Terminal({
|
||||
cursorStyle: "underline",
|
||||
cols: 180,
|
||||
rows: 50
|
||||
});
|
||||
term.open(document.querySelector('#terminal'));
|
||||
|
||||
|
||||
function newLine(t = true, prompt = true) {
|
||||
t && term.write('\r\n');
|
||||
cursorPosition = 0;
|
||||
if(prompt)
|
||||
{
|
||||
term.write(DEFAULT_PROMPT);
|
||||
cursorPosition += CLEAR_STYLING(DEFAULT_PROMPT).length;
|
||||
}
|
||||
resetUserSpace()
|
||||
}
|
||||
|
||||
let waitms = ms => new Promise(ok => setTimeout(()=>ok(), ms));
|
||||
|
||||
let cursorPosition = 0;
|
||||
let _buffer = [];
|
||||
let COLOR_RESET = () => "\x1B[0m";
|
||||
let COLOR_TEXT = (r,g,b) => `\x1B[38;2;${r};${g};${b}m`;
|
||||
let COLOR_BACKGROUND = (r,g,b) => `\x1B[48;2;${r};${g};${b}m`;
|
||||
|
||||
let CURSOR_MOVE = (row, col) => `\x1B[${row};${col}H`;
|
||||
let CURSOR_MOVE_UP = () => `\x1B[A`;
|
||||
let CURSOR_MOVE_DOWN = () => `\x1B[B`;
|
||||
let CURSOR_MOVE_RIGHT = () => `\x1B[C`;
|
||||
let CURSOR_MOVE_LEFT = () => `\x1B[D`;
|
||||
|
||||
let CLEAR_SCREEN = () => `\x1B[2J`;
|
||||
let CLEAR_LINE = () => `\x1B[2K`;
|
||||
|
||||
term.write(CURSOR_MOVE(2,1));
|
||||
|
||||
let CLEAR_STYLING = (text) => text.toString().replace(/\x1B(.*?)m/ig,'');
|
||||
|
||||
function writeHi()
|
||||
{
|
||||
term.write(
|
||||
"Merhaba, "
|
||||
+ COLOR_TEXT(0,255,0)
|
||||
+ COLOR_BACKGROUND(32,32,32)
|
||||
+ ' saQut RC Shell '
|
||||
+ COLOR_RESET()
|
||||
+ "\n\n\r"
|
||||
);
|
||||
|
||||
newLine();
|
||||
}
|
||||
|
||||
let userSpace = {
|
||||
buffer: [],
|
||||
paddingChars: 0,
|
||||
size: 0
|
||||
};
|
||||
|
||||
function resetUserSpace()
|
||||
{
|
||||
userSpace = {
|
||||
buffer: [],
|
||||
paddingChars: 0,
|
||||
size: 0
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentLine()
|
||||
{
|
||||
return term
|
||||
.buffer
|
||||
.active
|
||||
.getLine(
|
||||
term
|
||||
.buffer
|
||||
.active
|
||||
.cursorY
|
||||
)
|
||||
.translateToString()
|
||||
}
|
||||
|
||||
|
||||
let pausedInput = false;
|
||||
let activeProgram = false;
|
||||
|
||||
term.onData(async chars => {
|
||||
if(pausedInput)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
if(chars.length > 1)
|
||||
{
|
||||
return await DefaultProcess(chars);
|
||||
}
|
||||
|
||||
if(chars.length == 1)
|
||||
{
|
||||
if(activeProgram)
|
||||
{
|
||||
if(activeProgram._sys.idleChar)
|
||||
{
|
||||
return activeProgram._sys.idleChar.triggerChar(chars);
|
||||
}
|
||||
}
|
||||
return await DefaultProcess(chars);
|
||||
}else if(activeProgram){
|
||||
return
|
||||
}
|
||||
|
||||
let tokens = [];
|
||||
chars.toString().replace(/(\x1B.+?m)|(\x1B\[.+)|(.+)/igm, (_,a,b,c,d,e,f,g) => {
|
||||
let charx = a || b || c;
|
||||
if(charx)
|
||||
{
|
||||
if(/^(\x1B.*?m)$|^(\x1B\[.*)$/i.test(charx))
|
||||
{
|
||||
tokens.push([charx,])
|
||||
}else{
|
||||
tokens.push([,charx])
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const [code, chars] of tokens)
|
||||
{
|
||||
pausedInput = true;
|
||||
if(code)
|
||||
{
|
||||
await DefaultProcess(code);
|
||||
}else if(chars)
|
||||
{
|
||||
for (const char of chars.split(''))
|
||||
{
|
||||
await DefaultProcess(char);
|
||||
}
|
||||
}
|
||||
pausedInput = false;
|
||||
}
|
||||
})
|
||||
|
||||
async function DefaultProcess(char)
|
||||
{
|
||||
let contextchanged = false;
|
||||
switch(char)
|
||||
{
|
||||
// Backspace
|
||||
case '\x7f':
|
||||
if(userSpace.paddingChars > 0)
|
||||
{
|
||||
term.write('\b \b');
|
||||
cursorPosition--;
|
||||
|
||||
let after = userSpace.buffer.slice(0, userSpace.paddingChars - 1);
|
||||
let before = userSpace.buffer.slice(userSpace.paddingChars);
|
||||
userSpace.buffer = [
|
||||
...after,
|
||||
...before
|
||||
];
|
||||
userSpace.paddingChars--;
|
||||
userSpace.size--;
|
||||
contextchanged = true;
|
||||
|
||||
term.write(before.join('')+" \b");
|
||||
term.write(before.map(() => '\b').join(''));
|
||||
}
|
||||
break;
|
||||
// Enter
|
||||
case '\r':
|
||||
let command = userSpace.buffer.join('');
|
||||
|
||||
if(activeProgram && activeProgram._sys.idleReadline)
|
||||
{
|
||||
newLine(true, false);
|
||||
return activeProgram._sys.triggerReadline(command);
|
||||
}
|
||||
|
||||
let parsedCommand = ParseCommand(command);
|
||||
if(parsedCommand != false)
|
||||
{
|
||||
let program = parsedCommand.find(e => e.type == 'commandname').data;
|
||||
|
||||
pausedInput = true;
|
||||
|
||||
newLine(true, false);
|
||||
|
||||
await ExecuteCommand(
|
||||
program,
|
||||
parsedCommand
|
||||
);
|
||||
pausedInput = false;
|
||||
|
||||
newLine(false);
|
||||
}else{
|
||||
newLine();
|
||||
}
|
||||
break;
|
||||
case CURSOR_MOVE_LEFT():
|
||||
if(userSpace.paddingChars > 0)
|
||||
{
|
||||
userSpace.paddingChars--;
|
||||
term.write(CURSOR_MOVE_LEFT());
|
||||
}
|
||||
break;
|
||||
case CURSOR_MOVE_UP():
|
||||
case CURSOR_MOVE_DOWN():
|
||||
break;
|
||||
// Delete
|
||||
case "\x1B[3~":
|
||||
let after = userSpace.buffer.slice(0, userSpace.paddingChars);
|
||||
let before = userSpace.buffer.slice(userSpace.paddingChars + 1);
|
||||
term.write(before.join('')+" \b");
|
||||
term.write(before.map(() => '\b').join(''));
|
||||
userSpace.buffer = [
|
||||
...after,
|
||||
...before
|
||||
];
|
||||
userSpace.size--;
|
||||
contextchanged = true;
|
||||
break;
|
||||
case CURSOR_MOVE_RIGHT():
|
||||
if(userSpace.paddingChars < userSpace.size)
|
||||
{
|
||||
term.write(CURSOR_MOVE_RIGHT());
|
||||
userSpace.paddingChars++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
let printable = char.charCodeAt(0);
|
||||
if(printable < 32)
|
||||
{
|
||||
return
|
||||
}
|
||||
// insert or append ?
|
||||
if(userSpace.paddingChars == userSpace.size)
|
||||
{
|
||||
term.write(char);
|
||||
|
||||
userSpace.buffer = [
|
||||
...userSpace.buffer.slice(0, userSpace.paddingChars),
|
||||
char,
|
||||
...userSpace.buffer.slice(userSpace.paddingChars),
|
||||
];
|
||||
|
||||
userSpace.paddingChars++;
|
||||
userSpace.size++;
|
||||
cursorPosition++;
|
||||
contextchanged = true;
|
||||
|
||||
}else{
|
||||
|
||||
let after = userSpace.buffer.slice(0, userSpace.paddingChars);
|
||||
let before = userSpace.buffer.slice(userSpace.paddingChars);
|
||||
|
||||
userSpace.buffer = [
|
||||
...after,
|
||||
char,
|
||||
...before
|
||||
];
|
||||
|
||||
term.write(char);
|
||||
term.write(before.join(''));
|
||||
term.write(before.map(() => '\b').join(''));
|
||||
contextchanged = true;
|
||||
userSpace.paddingChars++;
|
||||
userSpace.size++;
|
||||
cursorPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
if(!activeProgram)
|
||||
{
|
||||
contextchanged && COLORIZE_LINE();
|
||||
}
|
||||
};
|
||||
|
||||
async function COLORIZE_LINE()
|
||||
{
|
||||
let before = userSpace.buffer.slice(0, userSpace.paddingChars);
|
||||
let after = userSpace.buffer.slice(userSpace.paddingChars);
|
||||
let coloredCommand = CommandColorize(before.concat(after).join(''));
|
||||
|
||||
if(coloredCommand)
|
||||
{
|
||||
// Cursoru en başa al
|
||||
term.write(CURSOR_MOVE_LEFT().repeat(before.length));
|
||||
term.write(coloredCommand);
|
||||
term.write(CURSOR_MOVE_LEFT().repeat(after.length));
|
||||
}
|
||||
}
|
||||
|
||||
let USER_SPACE = "virtualhost";
|
||||
let USER_NAME = [
|
||||
147 + Math.random() * 25 | 0,
|
||||
200 + Math.random() * 56 | 0,
|
||||
0 + Math.random() * 256 | 0,
|
||||
0 + Math.random() * 256 | 0
|
||||
].join('.');
|
||||
|
||||
let DEFAULT_PROMPT = (
|
||||
COLOR_RESET()
|
||||
+ '['
|
||||
+ COLOR_TEXT(0,255,0)
|
||||
+ USER_SPACE
|
||||
+ ' '
|
||||
+ COLOR_TEXT(0,128,255)
|
||||
+ USER_NAME
|
||||
+ COLOR_RESET()
|
||||
+ '] $ '
|
||||
);
|
||||
|
||||
|
||||
writeHi();
|
||||
|
||||
let _color_command = [255,255,255];
|
||||
let _color_option = [128,128,255];
|
||||
let _color_stringdata = [128,255,64];
|
||||
|
||||
function CommandColorize(command)
|
||||
{
|
||||
let hmx = new Hemex();
|
||||
hmx.setText(command);
|
||||
|
||||
let output = [];
|
||||
|
||||
let commandBefore = hmx.of(() => !hmx.isLetter());
|
||||
|
||||
if(commandBefore.length != 0)
|
||||
{
|
||||
output.push(
|
||||
COLOR_TEXT(255,255,255),
|
||||
commandBefore,
|
||||
COLOR_RESET()
|
||||
)
|
||||
}
|
||||
|
||||
if(!hmx.isLetter())
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
let commandName = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
|
||||
|
||||
if(hmx.isEnd() && !hmx.isWhiteSpace())
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
output.push(
|
||||
COLOR_TEXT(..._color_command),
|
||||
commandName,
|
||||
COLOR_RESET()
|
||||
);
|
||||
|
||||
let all = [], stringstarted = false;
|
||||
hmx.each(() => {
|
||||
let char = hmx.getChar();
|
||||
switch(char)
|
||||
{
|
||||
case '\'':
|
||||
case '"':
|
||||
case '`':{
|
||||
if(stringstarted == false)
|
||||
{
|
||||
stringstarted = char;
|
||||
all.push(COLOR_TEXT(..._color_stringdata), char)
|
||||
}else if(stringstarted == char){
|
||||
stringstarted = false;
|
||||
all.push(char, COLOR_TEXT(..._color_option))
|
||||
}else{
|
||||
all.push(char)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:{
|
||||
if(all.length == 0)
|
||||
{
|
||||
all.push(COLOR_TEXT(..._color_option), char);
|
||||
}else{
|
||||
all.push(char)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
output.push(all.join(''))
|
||||
|
||||
output.push(COLOR_RESET())
|
||||
return output.join('')
|
||||
}
|
||||
|
||||
function ParseCommand(command)
|
||||
{
|
||||
let hmx = new Hemex();
|
||||
hmx.setText(command);
|
||||
|
||||
hmx.of(() => !hmx.isLetter());
|
||||
|
||||
if(!hmx.isLetter())
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
let onlyArgs = false;
|
||||
|
||||
let commandName = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
|
||||
|
||||
if(hmx.isEnd() && !hmx.isWhiteSpace())
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
hmx.readWhiteSpace();
|
||||
|
||||
let args = [];
|
||||
|
||||
let xargs = [], operation = 'option';
|
||||
|
||||
hmx.beginPosition();
|
||||
hmx.while(()=>{
|
||||
if(hmx.include('--') && operation == 'option')
|
||||
{
|
||||
xargs.length && (
|
||||
args.push({
|
||||
type: 'data',
|
||||
data: xargs.join('')
|
||||
}),
|
||||
xargs = []
|
||||
);
|
||||
|
||||
hmx.beginPosition();
|
||||
|
||||
hmx.include('--', true);
|
||||
let argumentkey = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
|
||||
|
||||
if(argumentkey == false)
|
||||
{
|
||||
hmx.rejectPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
args.push({
|
||||
type: "argument",
|
||||
data: argumentkey
|
||||
});
|
||||
|
||||
hmx.acceptPosition();
|
||||
|
||||
hmx.readWhiteSpace();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(hmx.include('-') && operation == 'option')
|
||||
{
|
||||
xargs.length && (
|
||||
args.push({
|
||||
type: 'data',
|
||||
data: xargs.join('').trim()
|
||||
}),
|
||||
xargs = []
|
||||
);
|
||||
|
||||
hmx.beginPosition();
|
||||
hmx.nextChar();
|
||||
let argumentkey = hmx.of(() => hmx.isLetter() || hmx.isNumber());
|
||||
|
||||
if(argumentkey == false)
|
||||
{
|
||||
hmx.rejectPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
argumentkey.split('').forEach(e => {
|
||||
args.push({
|
||||
type: "flag",
|
||||
data: e
|
||||
})
|
||||
})
|
||||
|
||||
hmx.acceptPosition();
|
||||
|
||||
hmx.readWhiteSpace();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let char;
|
||||
if((char = hmx.includes(['\'','"'])) && operation == 'option')
|
||||
{
|
||||
xargs.length && (
|
||||
args.push({
|
||||
type: 'data',
|
||||
data: xargs.join('')
|
||||
}),
|
||||
xargs = []
|
||||
);
|
||||
|
||||
let escapeChar = char[0];
|
||||
hmx.nextChar();
|
||||
|
||||
let data = [];
|
||||
|
||||
hmx.while(() => {
|
||||
let char = hmx.getChar();
|
||||
switch(hmx.getChar())
|
||||
{
|
||||
case escapeChar:{
|
||||
hmx.nextChar();
|
||||
return false;
|
||||
}
|
||||
default:{
|
||||
data.push(char);
|
||||
hmx.nextChar();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
args.push({
|
||||
type: "string",
|
||||
data: data.join('')
|
||||
})
|
||||
|
||||
hmx.readWhiteSpace();
|
||||
|
||||
return true;
|
||||
}
|
||||
xargs.push(hmx.getChar());
|
||||
if(hmx.isWhiteSpace())
|
||||
{
|
||||
operation = 'option';
|
||||
}else{
|
||||
operation = 'data';
|
||||
}
|
||||
|
||||
hmx.nextChar();
|
||||
|
||||
if(hmx.isEnd())
|
||||
{
|
||||
return true;
|
||||
}else{
|
||||
args.push({
|
||||
type: "data",
|
||||
data: xargs.join('')
|
||||
})
|
||||
return false;
|
||||
}
|
||||
});
|
||||
onlyArgs = hmx.getPositionRange();
|
||||
hmx.acceptPosition();
|
||||
|
||||
return [{
|
||||
type: "cmdline",
|
||||
data: command
|
||||
},{
|
||||
type: "commandname",
|
||||
data: commandName
|
||||
},{
|
||||
type: "arguments",
|
||||
data: onlyArgs
|
||||
}, ...args]
|
||||
}
|
||||
|
||||
function generatepipe()
|
||||
{
|
||||
pausedInput = true;
|
||||
|
||||
let events = {};
|
||||
|
||||
let gen = {
|
||||
stdin: {
|
||||
setPaused(boolean){
|
||||
pausedInput = Boolean(boolean);
|
||||
},
|
||||
async readline(){
|
||||
gen.stdin.setPaused(false);
|
||||
gen._sys.idleReadline = true;
|
||||
return await new Promise(ok => {
|
||||
events['readline'] = (result) => {
|
||||
gen.stdin.setPaused(true);
|
||||
gen._sys.idleReadline = false;
|
||||
ok(result)
|
||||
events['readline'] = null;
|
||||
};
|
||||
})
|
||||
},
|
||||
async getchar(){
|
||||
gen.stdin.setPaused(false);
|
||||
gen._sys.idleChar = true;
|
||||
return await new Promise(ok => {
|
||||
events['read'] = (result) => {
|
||||
gen.stdin.setPaused(true);
|
||||
gen._sys.idleChar = false;
|
||||
events['read'] = null;
|
||||
ok(result)
|
||||
};
|
||||
})
|
||||
}
|
||||
},
|
||||
stdout: {
|
||||
async write(...args){
|
||||
term.write(args.map(e => e.toString()).join(' '))
|
||||
},
|
||||
async writeln(...args){
|
||||
term.writeln(args.map(e => e.toString()).join(' '))
|
||||
},
|
||||
COLOR_RESET,
|
||||
COLOR_TEXT,
|
||||
COLOR_BACKGROUND,
|
||||
CURSOR_MOVE,
|
||||
CURSOR_MOVE_UP,
|
||||
CURSOR_MOVE_DOWN,
|
||||
CURSOR_MOVE_RIGHT,
|
||||
CURSOR_MOVE_LEFT,
|
||||
CLEAR_SCREEN,
|
||||
CLEAR_LINE,
|
||||
CLEAR_STYLING
|
||||
},
|
||||
stderr: {
|
||||
async write(...args){
|
||||
term.write(COLOR_TEXT(255,0,0));
|
||||
term.write(args.map(e => e.toString()).join(' '))
|
||||
term.write(COLOR_RESET());
|
||||
},
|
||||
async writeln(...args){
|
||||
term.write(COLOR_TEXT(255,0,0));
|
||||
term.writeln(args.map(e => e.toString()).join(' '))
|
||||
term.write(COLOR_RESET());
|
||||
}
|
||||
},
|
||||
_sys:{
|
||||
idleReadline: false,
|
||||
idleChar: false,
|
||||
triggerReadline(text){
|
||||
if(events['readline']) events['readline'](text)
|
||||
},
|
||||
triggerChar(text){
|
||||
if(events['read']) events['read'](text)
|
||||
},
|
||||
destroy(){
|
||||
gen.stdin.setPaused(false);
|
||||
events = void 0;
|
||||
gen._sys.idleReadline = false;
|
||||
gen._sys.idleChar = false;
|
||||
gen._sys = void 0;
|
||||
gen.stderr = void 0;
|
||||
gen.stdin = void 0;
|
||||
gen.stdout = void 0;
|
||||
activeProgram = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
activeProgram = gen;
|
||||
|
||||
return gen;
|
||||
}
|
||||
|
||||
async function ExecuteCommand(command, cmdline)
|
||||
{
|
||||
let Namespace = await CommandNamespace(command);
|
||||
if(Namespace == null)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await new Promise(async (ok,reject) => {
|
||||
resetUserSpace();
|
||||
let program = new Namespace(generatepipe());
|
||||
program.exit = () => ok();
|
||||
try{
|
||||
let t = program.main(cmdline);
|
||||
if(t?.constructor?.name == 'AsyncFunction')
|
||||
{
|
||||
await t;
|
||||
}
|
||||
}catch(e){
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(exception)
|
||||
{
|
||||
term.writeln('');
|
||||
term.write(COLOR_TEXT(255,0,0));
|
||||
term.writeln('Yazılım hatası:');
|
||||
term.writeln('=========================');
|
||||
term.write(COLOR_RESET());
|
||||
term.writeln(exception.message);
|
||||
term.writeln(exception.stack.replace(/\n/g,'\r\n'));
|
||||
term.write(COLOR_TEXT(255,0,0));
|
||||
term.writeln('=========================');
|
||||
term.write(COLOR_RESET());
|
||||
}
|
||||
finally
|
||||
{
|
||||
pausedInput = false;
|
||||
activeProgram = void 0;
|
||||
}
|
||||
}
|
||||
|
||||
async function InspectRepo()
|
||||
{
|
||||
if(CommandNamespace.repos.find(e => e.downloaded == false))
|
||||
{
|
||||
term.writeln(COLOR_RESET()+"\r\nUpdating repos....")
|
||||
await wait(100);
|
||||
}else{
|
||||
term.writeln('');
|
||||
}
|
||||
for(const repo of CommandNamespace.repos)
|
||||
{
|
||||
if(repo.downloaded == false)
|
||||
{
|
||||
term.writeln(`Downloading:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
||||
await wait(100);
|
||||
let file = await DownloadRepoMetapack(repo.endpoint);
|
||||
repo.packages = file;
|
||||
term.writeln(`Downloaded:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
||||
await wait(100);
|
||||
repo.downloaded = true;
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
async function CommandNamespace(command)
|
||||
{
|
||||
let package = false;
|
||||
if(!CommandNamespace.namespaces.has(command))
|
||||
{
|
||||
await InspectRepo();
|
||||
for(const repo of CommandNamespace.repos)
|
||||
{
|
||||
for (const repopackage in repo.packages)
|
||||
{
|
||||
let packageName = repopackage;
|
||||
let meta = repo.packages[repopackage];
|
||||
let _command = meta.commands.find(_command => command == _command);
|
||||
|
||||
if(_command)
|
||||
{
|
||||
package = [packageName,meta];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(package == false)
|
||||
{
|
||||
term.writeln(COLOR_TEXT(255,0,0) + "'" + command + "' not found !" + COLOR_RESET());
|
||||
}else{
|
||||
term.writeln([
|
||||
COLOR_TEXT(0,255,0),
|
||||
package[0],
|
||||
COLOR_TEXT(200,200,200),
|
||||
" paketine ait olan ",
|
||||
COLOR_TEXT(0,255,0),
|
||||
command,
|
||||
COLOR_TEXT(200,200,200),
|
||||
" komutunu kullanabilmek için aşağıdaki komutunu çalıştırın"
|
||||
].join('') +
|
||||
"\r\n\n" +
|
||||
CommandColorize(`load '${package[0]}' from 'official'`)+
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
}else{
|
||||
return CommandNamespace.namespaces.get(command);
|
||||
}
|
||||
}
|
||||
|
||||
function wait(ms)
|
||||
{
|
||||
return new Promise(ok => setTimeout(() => ok(),ms));
|
||||
}
|
||||
|
||||
async function DownloadRepoMetapack(endpoint)
|
||||
{
|
||||
let request = await fetch(endpoint,{
|
||||
method: "get",
|
||||
cache: "force-cache",
|
||||
priority: "high",
|
||||
redirect: "follow",
|
||||
referrerPolicy: "no-referrer"
|
||||
});
|
||||
|
||||
await wait(100);
|
||||
|
||||
try{
|
||||
let response = await request.json();
|
||||
return response;
|
||||
}catch
|
||||
{
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
CommandNamespace.namespaces = new Map();
|
||||
CommandNamespace.repos = [{
|
||||
name: "official",
|
||||
endpoint: new URL('/console/official/packages.json',window.location),
|
||||
downloaded: false,
|
||||
packages: {}
|
||||
}];
|
||||
|
||||
|
||||
|
||||
class SystemLoad{
|
||||
stdin = null;
|
||||
stdout = null;
|
||||
constructor(pipe){
|
||||
this.stdin = pipe.stdin;
|
||||
this.stdout = pipe.stdout;
|
||||
}
|
||||
printhelp()
|
||||
{
|
||||
this.stdout.writeln('Kullanım şekli: ');
|
||||
this.stdout.writeln('ExampleProgram paketini kurmak için aşağıdaki komutu yazmalısınız\r\n');
|
||||
this.stdout.writeln(
|
||||
CommandColorize(`load 'ExampleProgram'`)+"\r\n"
|
||||
);
|
||||
|
||||
}
|
||||
async main(args)
|
||||
{
|
||||
let m = false, nargs = args.filter(({type}) => {
|
||||
if(type == "arguments") return m = true, false;
|
||||
return m;
|
||||
});
|
||||
|
||||
let packname;
|
||||
if(nargs[0].type == "string")
|
||||
{
|
||||
packname = nargs[0].data;
|
||||
}else{
|
||||
this.printhelp();
|
||||
this.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
if(CommandNamespace.repos.find(e => e.downloaded == false))
|
||||
{
|
||||
this.stdout.writeln(COLOR_RESET()+"\r\nUpdating repos....")
|
||||
}else{
|
||||
this.stdout.writeln('');
|
||||
}
|
||||
for(const repo of CommandNamespace.repos)
|
||||
{
|
||||
if(repo.downloaded == false)
|
||||
{
|
||||
this.stdout.writeln(`Downloading:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
||||
await wait(100);
|
||||
let file = await DownloadRepoMetapack(repo.endpoint.href);
|
||||
if(file == null){
|
||||
continue
|
||||
}
|
||||
repo.packages = file;
|
||||
this.stdout.writeln(`Downloaded:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
||||
await wait(100);
|
||||
repo.downloaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
let endpoints = CommandNamespace.repos.map(e => e.packages[packname] && e.packages[packname].endpoints.map(endpoint => new URL(endpoint,e.endpoint))).filter(e => !!e)
|
||||
|
||||
if(endpoints.length == 0)
|
||||
{
|
||||
term.writeln(COLOR_TEXT(255,0,0) + "'" + packname + "' repo not found !" + COLOR_RESET());
|
||||
await wait(100);
|
||||
this.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const endpoint of endpoints[0])
|
||||
{
|
||||
let href = endpoint.href;
|
||||
this.stdout.writeln(`Packet downloading :${COLOR_TEXT(0,255,0)} ${packname} ${COLOR_RESET()} ${endpoint.href}`);
|
||||
await wait(100);
|
||||
|
||||
let request = await fetch(href,{
|
||||
method: "get",
|
||||
cache: "force-cache",
|
||||
priority: "high",
|
||||
redirect: "follow",
|
||||
referrerPolicy: "no-referrer"
|
||||
});
|
||||
|
||||
this.stdout.writeln(`Packet unzip to :${COLOR_TEXT(0,255,0)} /mount/xpack/${packname}/ ${COLOR_RESET()}`);
|
||||
await wait(100);
|
||||
|
||||
let script = await request.text();
|
||||
|
||||
try{
|
||||
this.stdout.writeln(`Analyzing..`);
|
||||
await new Promise(ok => setTimeout(() => ok(),1000));
|
||||
(new Function(script))();
|
||||
this.stdout.writeln(`Process success`);
|
||||
await wait(100);
|
||||
}catch(error){
|
||||
debugger;
|
||||
this.stderr.writeln(`Process error`);
|
||||
await wait(100);
|
||||
}
|
||||
}
|
||||
this.exit();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CommandNamespace.namespaces.set('load', SystemLoad);
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
class CommandJS{
|
||||
stdin = null;
|
||||
stdout = null;
|
||||
stderr = null;
|
||||
constructor(pipe){
|
||||
this.stdin = pipe.stdin;
|
||||
this.stdout = pipe.stdout;
|
||||
this.stderr = pipe.stderr;
|
||||
}
|
||||
async main(args)
|
||||
{
|
||||
let commandline = args.find(e => e.type == "arguments");
|
||||
if(commandline)
|
||||
{
|
||||
try{
|
||||
let t = eval(commandline.data);
|
||||
if(typeof t != "undefined")
|
||||
{
|
||||
this.stdout.writeln(JSON.stringify(t,null, ' ').replace(/\n/g,'\r\n'));
|
||||
}
|
||||
}catch(e){
|
||||
this.stderr.writeln(e.message);
|
||||
}
|
||||
}
|
||||
this.exit();
|
||||
}
|
||||
};
|
||||
|
||||
class CommandJSRaw{
|
||||
stdin = null;
|
||||
stdout = null;
|
||||
stderr = null;
|
||||
constructor(pipe){
|
||||
this.stdin = pipe.stdin;
|
||||
this.stdout = pipe.stdout;
|
||||
this.stderr = pipe.stderr;
|
||||
}
|
||||
async main(args)
|
||||
{
|
||||
let commandline = args.find(e => e.type == "arguments");
|
||||
if(commandline)
|
||||
{
|
||||
let t = eval(commandline.data);
|
||||
this.stdout.writeln(t);
|
||||
}
|
||||
this.exit();
|
||||
}
|
||||
};
|
||||
|
||||
CommandNamespace.namespaces.set('js', CommandJS);
|
||||
CommandNamespace.namespaces.set('jsecho', CommandJSRaw);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
class CommandSet{
|
||||
stdin = null;
|
||||
stdout = null;
|
||||
stderr = null;
|
||||
constructor(pipe){
|
||||
this.stdin = pipe.stdin;
|
||||
this.stdout = pipe.stdout;
|
||||
this.stderr = pipe.stderr;
|
||||
}
|
||||
async main(args)
|
||||
{
|
||||
this.stdout.writeln(JSON.stringify(args,null, ' ').replace(/\n/g,'\r\n'));
|
||||
this.exit();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CommandNamespace.namespaces.set('set', CommandSet);
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"native": {
|
||||
"endpoints": [
|
||||
"./native/js.js",
|
||||
"./native/set.js"
|
||||
],
|
||||
"commands": [
|
||||
"js",
|
||||
"jsecho",
|
||||
"set"
|
||||
]
|
||||
},
|
||||
"rtc": {
|
||||
"endpoints": [
|
||||
"./rtc/mwse-socket.js",
|
||||
"./rtc/mwse-close.js"
|
||||
],
|
||||
"commands": [
|
||||
"mwse-socket",
|
||||
"mwse-close"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
Oluşturulmuş cihazlar | device list
|
||||
Yeni AudioContext | device create audio /dev/audio0
|
||||
Yeni HTMLVideoElement | device create video /dev/video0
|
||||
getUserMedia isteği | webcam 480x360 --audio /dev/audio0 --video /dev/video0
|
||||
AudioContext sesi dışarı verme | device /dev/audio0 --output speaker
|
||||
AudioContext sesi açma | device /dev/audio0 --value 100
|
||||
Videoyu oynatma | lightplayer --source /dev/video0 --target popup
|
||||
|
||||
|
||||
Tüm bağlantıları görme | rtc connections
|
||||
RTC Oluşturma | rtc create "peer-0"
|
||||
RTC offer verme | rtc "peer-0" create offer
|
||||
RTC offer alma | rtc "peer-0" emit offer "offer message"
|
||||
RTC answer verme | rtc "peer-0" create answer
|
||||
RTC answer alma | rtc "peer-0" emit answer "answer message"
|
||||
RTC answer verme | rtc "peer-0" create answer "answer message"
|
||||
RTC candidate alma | rtc "peer-0" getcandidate --all
|
||||
RTC candidate verme | rtc "peer-0" setcandidate
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
class MWSECloser{
|
||||
stdin = null;
|
||||
stdout = null;
|
||||
stderr = null;
|
||||
|
||||
endpoint = null;
|
||||
static mwse = null;
|
||||
|
||||
constructor(pipe){
|
||||
this.stdin = pipe.stdin;
|
||||
this.stdout = pipe.stdout;
|
||||
this.stderr = pipe.stderr;
|
||||
}
|
||||
greenText(text){
|
||||
this.stdout.writeln(`${this.stdout.COLOR_TEXT(0,255,0)}${text}${this.stdout.COLOR_RESET()}`);
|
||||
}
|
||||
redText(text){
|
||||
this.stdout.writeln(`${this.stdout.COLOR_TEXT(255,0,0)}${text}${this.stdout.COLOR_RESET()}`);
|
||||
}
|
||||
async main(args)
|
||||
{
|
||||
let mwseConnector = CommandNamespace.namespaces.get('mwse-socket');
|
||||
if(mwseConnector == null){
|
||||
this.stderr.writeln(`mwse-socket yok`);
|
||||
return this.exit();
|
||||
}
|
||||
if(mwseConnector.mwse == null){
|
||||
this.stderr.writeln(`Açık bir soket bulunamadı`);
|
||||
return this.exit();
|
||||
}
|
||||
if(mwseConnector.mwse.server?.disconnect == null){
|
||||
this.stderr.writeln(`MWSE bağlayıcısında hata oluştu`);
|
||||
return this.exit();
|
||||
}
|
||||
mwseConnector.mwse.server.disconnect();
|
||||
this.greenText("MWSE socketi başarılı bir şekilde kapatıldı");
|
||||
this.redText(`\n\n${mwseConnector.mwse.server.endpoint.host} sunucusuyla MWSE bağlantısı kesildi\n\n`);
|
||||
return this.exit();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CommandNamespace.namespaces.set('mwse-close', MWSECloser);
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
class MWSECloser{
|
||||
stdin = null;
|
||||
stdout = null;
|
||||
stderr = null;
|
||||
|
||||
endpoint = null;
|
||||
static mwse = null;
|
||||
|
||||
constructor(pipe){
|
||||
this.stdin = pipe.stdin;
|
||||
this.stdout = pipe.stdout;
|
||||
this.stderr = pipe.stderr;
|
||||
}
|
||||
greenText(text){
|
||||
this.stdout.writeln(`${this.stdout.COLOR_TEXT(0,255,0)}${text}${this.stdout.COLOR_RESET()}`);
|
||||
}
|
||||
async main(args)
|
||||
{
|
||||
this.stdout.write("Endpoint: ");
|
||||
let answer = await this.stdin.readline();
|
||||
let url;
|
||||
|
||||
try{
|
||||
url = new URL(answer)
|
||||
}catch{
|
||||
this.stderr.writeln('Bilinmeyen path yapısı');
|
||||
return this.exit();
|
||||
};
|
||||
|
||||
this.greenText('Path doğrulandı');
|
||||
|
||||
if(url.protocol.toLowerCase() != 'wss:' && url.protocol.toLowerCase() != 'ws:'){
|
||||
this.stderr.writeln('Bilinmeyen protokol');
|
||||
return this.exit();
|
||||
}
|
||||
|
||||
this.greenText('Şema doğrulandı');
|
||||
|
||||
this.endpoint = url;
|
||||
|
||||
try{
|
||||
await this.scriptImport();
|
||||
}catch{
|
||||
this.stderr.writeln('MWSE bağlayıcısı indirilemedi');
|
||||
return this.exit();
|
||||
};
|
||||
|
||||
this.greenText(`${url.host} üzerinden bağlanılıyor : ${url.href}`);
|
||||
let mwse;
|
||||
try{
|
||||
mwse = new MWSE({
|
||||
endpoint: url.href
|
||||
});
|
||||
|
||||
await new Promise(ok => {
|
||||
mwse.scope(async ()=>{
|
||||
this.greenText(`\n\nMWSE üzerinden ${url.host} makinesine bağlısınız\n\n`);
|
||||
ok();
|
||||
})
|
||||
});
|
||||
|
||||
MWSECloser.mwse = mwse;
|
||||
}catch{
|
||||
this.stderr.writeln(`${url.host} makinesine bağlanırken bir sorun oluştu`);
|
||||
return this.exit();
|
||||
}finally{
|
||||
this.exit();
|
||||
}
|
||||
}
|
||||
async scriptImport()
|
||||
{
|
||||
let t = new URL("script",this.endpoint);
|
||||
if(t.protocol == 'wss:'){
|
||||
t.protocol = 'https:';
|
||||
}else{
|
||||
t.protocol = 'http:';
|
||||
}
|
||||
let script = document.createElement("script");
|
||||
await new Promise((ok,rej) => {
|
||||
script.onload = () => ok();
|
||||
script.onerror = () => rej();
|
||||
script.setAttribute("src",t.href);
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
if(window.MWSE === void 0){
|
||||
this.stderr.writeln('MWSE bağlayıcısı indirilemedi');
|
||||
return this.exit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CommandNamespace.namespaces.set('mwse-socket', MWSECloser);
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:0}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,9 +1,8 @@
|
|||
import WebRTC from "./WebRTC";
|
||||
import Peer from "./Peer";
|
||||
|
||||
/**
|
||||
* Deneyseldir kullanılması önerilmez
|
||||
*/
|
||||
|
||||
|
||||
export default class P2PFileSender
|
||||
{
|
||||
public rtc : RTCPeerConnection;
|
||||
|
|
|
|||
|
|
@ -219,12 +219,7 @@ export default class Peer extends EventTarget
|
|||
to: this.socketId
|
||||
});
|
||||
}else{
|
||||
if(pack.type != ':rtcpack:')
|
||||
{
|
||||
this.rtc?.sendMessage(pack)
|
||||
}else{
|
||||
return console.warn("Socket is not writable");
|
||||
}
|
||||
}
|
||||
}
|
||||
async forget(){
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default class WebRTC
|
|||
rtcpMuxPolicy:"require",
|
||||
};
|
||||
|
||||
private isPolite() : boolean
|
||||
public isPolite() : boolean
|
||||
{
|
||||
let myId = this.peer?.mwse.peer('me').socketId as string;
|
||||
let peerId = this.peer?.socketId as string;
|
||||
|
|
@ -201,7 +201,6 @@ export default class WebRTC
|
|||
};
|
||||
sendingStream.senders = senders;
|
||||
}
|
||||
this.emit('stream:accepted', sendingStream);
|
||||
break;
|
||||
}
|
||||
case "message":{
|
||||
|
|
@ -238,10 +237,6 @@ export default class WebRTC
|
|||
}
|
||||
public sendMessage(data: any)
|
||||
{
|
||||
if(data.type == ':rtcpack:')
|
||||
{
|
||||
throw "WebRTC Kanalında Sızma";
|
||||
}
|
||||
this.send({
|
||||
type: 'message',
|
||||
payload: data
|
||||
|
|
@ -296,7 +291,6 @@ export default class WebRTC
|
|||
this.channel = undefined;
|
||||
WebRTC.channels.delete(this.id);
|
||||
WebRTC.requireGC = true;
|
||||
this.active = false;
|
||||
})
|
||||
}else{
|
||||
this.emit('datachannel', event.channel);
|
||||
|
|
|
|||
|
|
@ -184,13 +184,13 @@ export default class MWSE extends EventTarget {
|
|||
let peer = this.peer(from, true);
|
||||
peer.info.info = info;
|
||||
peer.emit("accepted/pair", peer);
|
||||
this.peer('me').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, true);
|
||||
peer.emit("end/pair", info);
|
||||
this.peer('me').emit('end/pair', from, info);
|
||||
peer.emit("endPair", info);
|
||||
this.peer('me').emit('endPair', from, info);
|
||||
})
|
||||
}
|
||||
public room(options: IRoomOptions | string) : Room
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
/ Odaların kendi verilerinin bulunması
|
||||
> HTTP ile kişilere mesaj (request) mesaj iletimi
|
||||
> HTTP ile odalar ve kişiler hakkında veri alınabilmesi
|
||||
> Websoket ile http proxy uygulama (WSAuth)
|
||||
> Session WSAuth
|
||||
137
index.js
137
index.js
|
|
@ -1,14 +1,127 @@
|
|||
require("./Source/index");
|
||||
/** @type {import('node:cluster').Cluster} */
|
||||
const cluster = require("cluster");
|
||||
const os = require("os");
|
||||
let {randomUUID} = require("crypto");
|
||||
|
||||
process.on('unhandledRejection',(reason, promise)=>{
|
||||
console.log("Process unhandledRejection",{reason, promise})
|
||||
});
|
||||
process.on('rejectionHandled',(promise)=>{
|
||||
console.log("Process rejectionHandled",{promise})
|
||||
});
|
||||
process.on('multipleResolves',(type, promise, value)=>{
|
||||
console.log("Process multipleResolves",{type, promise, value})
|
||||
});
|
||||
process.on('warning',(err)=>{
|
||||
console.log("Process warning", err)
|
||||
/**
|
||||
* Use Round Robin algorithm for cluster process load balancer
|
||||
*/
|
||||
// cluster.schedulingPolicy = cluster.SCHED_RR;
|
||||
|
||||
async function main()
|
||||
{
|
||||
if(cluster.isPrimary == false)
|
||||
{
|
||||
console.log("Slave Process PID:", process.pid);
|
||||
// This process is a worker / slave
|
||||
// Compile source code and run
|
||||
require("./Source/index");
|
||||
// stay here
|
||||
return;
|
||||
};
|
||||
|
||||
// This process is a primary / master
|
||||
console.log("Master Process PID:", process.pid);
|
||||
|
||||
// Worker process list
|
||||
const master = new Map();
|
||||
|
||||
const coreCount = 1 //os.cpus().length;
|
||||
for(let index = 0; index < coreCount; index++)
|
||||
{
|
||||
// Open slave process
|
||||
let worker = await generateFlow();
|
||||
// Save process with id
|
||||
master.set(worker.id, worker);
|
||||
// Listen process for commands
|
||||
worker.message(
|
||||
// This process want to send payload to sibling process with IPC
|
||||
(workerId, payload) =>{
|
||||
// Check Target worker
|
||||
if(payload.core)
|
||||
{
|
||||
switch(payload.core)
|
||||
{
|
||||
case "writestat":{
|
||||
master.get(workerId).setStats(payload)
|
||||
break;
|
||||
}
|
||||
case "readstat":{
|
||||
master.get(workerId).send({
|
||||
type: ':stats:',
|
||||
data: [
|
||||
...master.entries()
|
||||
].map((
|
||||
[, master]
|
||||
) => {
|
||||
let e = master.getStats();
|
||||
return {
|
||||
core: master.uuid,
|
||||
ws_writed_bytes:e.ws_writed_bytes,
|
||||
ws_readed_bytes:e.ws_readed_bytes,
|
||||
ws_total_bytes:e.ws_total_bytes,
|
||||
ws_sended_packs:e.ws_sended_packs,
|
||||
ws_recaived_packs:e.ws_recaived_packs,
|
||||
ws_total_packs:e.ws_total_packs,
|
||||
mwse_rooms: e.mwse_rooms,
|
||||
mwse_clients: e.mwse_clients
|
||||
}
|
||||
})
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if(payload.process)
|
||||
{
|
||||
master.get(payload.process).send({
|
||||
...payload,
|
||||
pid: worker.id
|
||||
})
|
||||
}else for (const [siblingWorkerId,{send}] of master) {
|
||||
// No sending to itself
|
||||
if(workerId !== siblingWorkerId)
|
||||
{
|
||||
|
||||
// Send command to sibling with IPC
|
||||
send({
|
||||
...payload,
|
||||
pid: worker.id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function generateFlow()
|
||||
{
|
||||
// Mirror this process with for (low-level os multitasking)
|
||||
const worker = cluster.fork();
|
||||
// Wait process is online
|
||||
await new Promise(ok => {
|
||||
worker.addListener("online",()=> {
|
||||
ok()
|
||||
})
|
||||
});
|
||||
// Get process pid on the os
|
||||
let id = worker.process.pid;
|
||||
|
||||
let stats = {};
|
||||
|
||||
// Simplification wrapping send and get events with IPC's event functions
|
||||
return {
|
||||
id,
|
||||
uuid: randomUUID(),
|
||||
send: message => worker.send(message),
|
||||
message: (callback) => worker.addListener("message", e => callback(id,e)),
|
||||
getStats: () => stats,
|
||||
setStats: e => Object.assign(stats, e)
|
||||
}
|
||||
}
|
||||
|
||||
// Run immediately
|
||||
process.nextTick(main);
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
html,body{
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
body{
|
||||
background-color: #141414;
|
||||
overflow: hidden;
|
||||
}
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.root{
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
.videolist{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(40vh, 1fr));
|
||||
grid-template-rows: repeat(auto-fit, minmax(23vh, 1fr));
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.videolist .frame{
|
||||
border: solid 2px white;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
align-items: center;
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
padding: 1px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.videolist .frame > video{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.videolist .frame.active{
|
||||
border: solid 3px green;
|
||||
box-shadow: 0px 0px 20px -10px green;
|
||||
}
|
||||
video{
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #ffffff1f;
|
||||
}
|
||||
|
||||
.tool-container{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
}
|
||||
.tool-container > .tools{
|
||||
margin: auto;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.tool-container > .tools > button{
|
||||
border: none;
|
||||
color: white;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.tool-container > .tools > button:not(:hover){
|
||||
background-color: transparent;
|
||||
}
|
||||
.tool-container > .tools > button:hover{
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
.tool-container > .tools > button i{
|
||||
vertical-align: middle;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="tr">
|
||||
<head>
|
||||
<base href="/stream/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>saQüt Video Streaming</title>
|
||||
<script src="https://ws.saqut.com/script"></script>
|
||||
<link rel="stylesheet" href="./index.css?v=42">
|
||||
</head>
|
||||
<body>
|
||||
<div class="root">
|
||||
<div class="videolist">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="./index.js?v=42"></script>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
/**
|
||||
* @type {import("./MWSE/index").default}
|
||||
*/
|
||||
let mwse;
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
let mySocketId;
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
let roomid;
|
||||
/**
|
||||
* @type {import("./MWSE/Room").default}
|
||||
*/
|
||||
let room;
|
||||
/**
|
||||
* @type {MediaStream}
|
||||
*/
|
||||
let outgoingStream;
|
||||
/**
|
||||
* @type {HTMLDivElement}
|
||||
*/
|
||||
let videoContainer = document.querySelector(".videolist");
|
||||
let maxbitrate;
|
||||
let resulation;
|
||||
let activePeers = {}
|
||||
|
||||
let ofscreencanvas = document.createElement("canvas");
|
||||
|
||||
function connect()
|
||||
{
|
||||
mwse = new MWSE({
|
||||
endpoint: "wss://ws.saqut.com"
|
||||
});
|
||||
|
||||
MWSE.rtc.defaultICEServers = [{
|
||||
urls: "turn:20.166.82.187:3478",
|
||||
username: "turnserver",
|
||||
credential: "turnserver"
|
||||
},{
|
||||
urls: "stun:stun.l.google.com:19302"
|
||||
}];
|
||||
|
||||
mwse.scope(beginEngine);
|
||||
}
|
||||
|
||||
|
||||
|
||||
let interact = false;
|
||||
|
||||
setInterval(()=>{
|
||||
document.querySelectorAll(".soundon").forEach(e => (e.muted = 0,e.play()));
|
||||
},1000)
|
||||
|
||||
document.addEventListener("click",()=>{
|
||||
interact = true;
|
||||
document.querySelectorAll(".soundon").forEach(e => (e.muted = 0,e.play()));
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* @type {HTMLVideoElement}
|
||||
*/
|
||||
let activeVideo;
|
||||
|
||||
function templateVideo(name, stream,infinitedMute)
|
||||
{
|
||||
let t = new DOMParser().parseFromString(`
|
||||
<div class="frame">
|
||||
<video autoplay playsinline muted data-name="${name}">
|
||||
|
||||
</video>
|
||||
<!--div class="tool-container">
|
||||
<div class="tools">
|
||||
<button>
|
||||
<i class="material-icons">home</i>
|
||||
</button>
|
||||
<button>
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div--->
|
||||
</div>
|
||||
`,"text/html");
|
||||
|
||||
let i = t.querySelector("video");
|
||||
if(infinitedMute == true)
|
||||
{
|
||||
i.muted = 1;
|
||||
}else if(interact == false)
|
||||
{
|
||||
i.muted = 1;
|
||||
i.classList.add("soundon");
|
||||
}
|
||||
if(stream) i.srcObject = stream;
|
||||
return t.querySelector("div");
|
||||
}
|
||||
function addVideoList(name, stream, peer, infinitedMute)
|
||||
{
|
||||
if(!videoContainer.querySelector(`[name="${name}"]`))
|
||||
{
|
||||
let video = templateVideo(name, stream, infinitedMute);
|
||||
video.dataset.user = peer.socketId;
|
||||
videoContainer.appendChild(video);
|
||||
}
|
||||
}
|
||||
|
||||
function removeVideoList(name)
|
||||
{
|
||||
if(videoContainer.querySelector(`[data-user="${name}"]`))
|
||||
{
|
||||
let k = videoContainer.querySelector(`[data-user="${name}"]`);
|
||||
if(k.dataset.user == activeVideo?.dataset.user || !activeVideo)
|
||||
{
|
||||
activePeer = null;
|
||||
}
|
||||
k.remove();
|
||||
}
|
||||
}
|
||||
|
||||
async function beginEngine()
|
||||
{
|
||||
let me = mwse.peer("me");
|
||||
me.disablePairAuth();
|
||||
mySocketId = me.socketId;
|
||||
|
||||
let url = new URL(window.location);
|
||||
roomid = url.searchParams.get("room");
|
||||
|
||||
if(!!roomid == 0)
|
||||
{
|
||||
let hash = window.crypto.randomUUID();
|
||||
url.searchParams.set("room", hash);
|
||||
window.location = url.href;
|
||||
};
|
||||
|
||||
connectRoom(roomid);
|
||||
|
||||
if(url.searchParams.get("maxbitrate"))
|
||||
{
|
||||
let n = Number(url.searchParams.get("maxbitrate"));
|
||||
if(Number.isFinite(n) && !Number.isNaN(n))
|
||||
{
|
||||
maxbitrate = n;
|
||||
}else maxbitrate = 2500_000;
|
||||
}else maxbitrate = 2500_000;
|
||||
|
||||
if(url.searchParams.get("resulation"))
|
||||
{
|
||||
let n = Number(url.searchParams.get("resulation"));
|
||||
if(Number.isFinite(n) && !Number.isNaN(n))
|
||||
{
|
||||
resulation = n;
|
||||
}else resulation = 1.2;
|
||||
}else resulation = 1.2;
|
||||
};
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
connect()
|
||||
});
|
||||
|
||||
|
||||
async function startOutgoingWebcam()
|
||||
{
|
||||
let mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||
video: /*true*/{
|
||||
advanced: [
|
||||
{ width: { exact: 1920 } },
|
||||
{ width: { exact: 1600 } },
|
||||
{ width: { exact: 1366 } },
|
||||
{ width: { exact: 1280 } },
|
||||
{ width: { exact: 1024 } },
|
||||
{ width: { exact: 900 } },
|
||||
{ width: { exact: 800 } },
|
||||
{ width: { exact: 640 } },
|
||||
{ width: { exact: 320 } },
|
||||
{ width: { exact: 240 } }
|
||||
],
|
||||
facingMode: "user"
|
||||
}
|
||||
});
|
||||
outgoingStream = mediaStream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function connectRoom()
|
||||
{
|
||||
await startOutgoingWebcam();
|
||||
|
||||
room = mwse.room({
|
||||
name: roomid,
|
||||
joinType: "free",
|
||||
accessType: "private",
|
||||
description: "Private free joined room",
|
||||
ifexistsJoin: true,
|
||||
notifyActionEjected: true,
|
||||
notifyActionInvite: false,
|
||||
notifyActionJoined: true
|
||||
});
|
||||
await room.createRoom();
|
||||
|
||||
room.on("join", peer => IncomingPeer(peer,true));
|
||||
room.on("eject", peer => OutgoingPeer(peer));
|
||||
|
||||
for (const peer of await room.fetchPeers()) {
|
||||
if(peer.socketId != mwse.peer('me').socketId){
|
||||
IncomingPeer(peer,false)
|
||||
}
|
||||
}
|
||||
|
||||
addVideoList("My Webcam",outgoingStream, mwse.peer("me"), true)
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import("./MWSE/Peer").default} peer
|
||||
*/
|
||||
function IncomingPeer(peer,activeConnect)
|
||||
{
|
||||
if(activeConnect)
|
||||
{
|
||||
peer.rtc.connect();
|
||||
}
|
||||
peer.rtc.rtc.turboBitrate = 0;
|
||||
peer.rtc.on('connected',() => {
|
||||
console.log("Connected");
|
||||
if(!activeConnect)
|
||||
{
|
||||
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
||||
activePeers[peer.socketId] = peer.rtc.rtc;
|
||||
}
|
||||
});
|
||||
peer.rtc.on('disconnected',() => {
|
||||
console.log("Disconnected");
|
||||
removeVideoList(peer.streamY, peer);
|
||||
delete activePeers[peer.socketId];
|
||||
});
|
||||
peer.rtc.on("stream:added", ({stream,name}) => {
|
||||
peer.streamY = peer.socketId + " | " + name + " - " + stream.id;
|
||||
addVideoList(peer.socketId + " | " + name + " - " + stream.id,stream, peer);
|
||||
if(activeConnect)
|
||||
{
|
||||
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* @param {import("./MWSE/Peer").default} peer
|
||||
*/
|
||||
function OutgoingPeer(peer)
|
||||
{
|
||||
removeVideoList(peer.socketId, peer);
|
||||
}
|
||||
|
||||
let relative;
|
||||
|
||||
|
||||
|
||||
setInterval(() => {
|
||||
for(const [,peerRtc] of Object.entries(activePeers))
|
||||
{
|
||||
if(peerRtc?.turboBitrate !== 1)
|
||||
{
|
||||
const senders = peerRtc.getSenders();
|
||||
const videoSender = senders.find(sender => sender.track?.kind === 'video');
|
||||
if(videoSender){
|
||||
const parameters = videoSender.getParameters();
|
||||
parameters.encodings[0].maxBitrate = maxbitrate;
|
||||
parameters.encodings[0].scaleResolutionDownBy = resulation;
|
||||
videoSender.setParameters(parameters).then(() => {
|
||||
peerRtc.turboBitrate = 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},1000);
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
function HostListItem()
|
||||
{
|
||||
let item = $(`
|
||||
<tr>
|
||||
<td class="text-nowrap">
|
||||
<button class="btn btn-outline-primary btn-sm action-download">
|
||||
İndir
|
||||
</button>
|
||||
<button class="btn btn-outline-danger btn-sm action-remove">
|
||||
Kaldır
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<button class="btn btn-outline-secondary btn-sm action-sha1">
|
||||
Hesapla
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<span class="action-filename">DCIM_AMG_25TEM2025.png</span>
|
||||
<br>
|
||||
<span class="text-muted">
|
||||
(Sizin cihazda)
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<div class="progress" style="height:30px">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
return {
|
||||
download: item.find(".action-download"),
|
||||
hide: item.find(".action-hide"),
|
||||
remove: item.find(".action-remove"),
|
||||
sha1: item.find(".action-sha1"),
|
||||
filename: item.find(".action-filename"),
|
||||
progress: item.find(".progress"),
|
||||
container: item
|
||||
}
|
||||
}
|
||||
|
||||
function RemoteListItem()
|
||||
{
|
||||
let item = $(`
|
||||
<tr>
|
||||
<td class="text-nowrap">
|
||||
<button class="btn btn-outline-primary btn-sm action-download">
|
||||
İndir
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm action-hide">
|
||||
Gizle
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<button class="btn btn-outline-secondary btn-sm action-sha1">
|
||||
Hesapla
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<span class="action-filename">DCIM.mp4</span>
|
||||
<br>
|
||||
<span class="text-muted">
|
||||
(Karşı cihazda)
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<div class="progress" style="height:30px">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
return {
|
||||
download: item.find(".action-download"),
|
||||
hide: item.find(".action-hide"),
|
||||
remove: item.find(".action-remove"),
|
||||
sha1: item.find(".action-sha1"),
|
||||
filename: item.find(".action-filename"),
|
||||
progress: item.find(".progress"),
|
||||
container: item
|
||||
}
|
||||
}
|
||||
|
||||
function EmptyListItem()
|
||||
{
|
||||
return $(`
|
||||
<tr>
|
||||
<td class="text-center text-muted" colspan="4">
|
||||
Gönderilecek veya alınacak dosya bulunmuyor
|
||||
</td>
|
||||
</tr>
|
||||
`)
|
||||
}
|
||||
|
||||
function formatBytes(a,b=2){if(!+a)return"0 Bytes";const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));return`${parseFloat((a/Math.pow(1024,d)).toFixed(c))} ${["Bytes","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"][d]}`}
|
||||
|
||||
|
||||
const endpoint = `wss://ws.saqut.com/socket/webserver/${("000000"+(Math.random() * 512 | 0)).slice(-6)}.sock`;
|
||||
|
||||
let mwse = new MWSE({
|
||||
endpoint,
|
||||
autoReconnect: true,
|
||||
autoReconnectTimeout: 60_000
|
||||
});
|
||||
|
||||
mwse.scope(Connect);
|
||||
|
||||
let room;
|
||||
let ip;
|
||||
|
||||
async function Connect()
|
||||
{
|
||||
await genRoom();
|
||||
$(".network-id").text(ip);
|
||||
}
|
||||
|
||||
async function genRoom()
|
||||
{
|
||||
do{
|
||||
let vip = ip = [
|
||||
(Math.random() * 16 | 0) + 10,
|
||||
(Math.random() * 8 | 0),
|
||||
(Math.random() * 4 | 0),
|
||||
(Math.random() * 254 | 0) + 1
|
||||
].join('.');
|
||||
room = mwse.room({
|
||||
name: `WEB3-Smash [${vip}] RO CONNECT:2`,
|
||||
description: "Web3 Smash saQut Server Application",
|
||||
joinType: "free",
|
||||
notifyActionJoined: true,
|
||||
notifyActionEjected: true,
|
||||
ifexistsJoin: false
|
||||
});
|
||||
try{
|
||||
await room.createRoom();
|
||||
break;
|
||||
}catch{
|
||||
continue;
|
||||
}
|
||||
}while(1);
|
||||
}
|
||||
|
||||
class FileDescriptor {
|
||||
static hostfiles = new Map();
|
||||
static remotefiles = new Map();
|
||||
static hostindex = 0;
|
||||
static remoteindex = 0;
|
||||
|
||||
file = null;
|
||||
listItem = null;
|
||||
|
||||
constructor(isHost, item,file){
|
||||
this.file = file;
|
||||
this.listItem = item;
|
||||
$("#thelist").append(item.container);
|
||||
if(isHost)
|
||||
{
|
||||
FileDescriptor.hostfiles.set(
|
||||
++FileDescriptor.hostindex,
|
||||
this
|
||||
);
|
||||
}else{
|
||||
FileDescriptor.remotefiles.set(
|
||||
++FileDescriptor.remoteindex,
|
||||
this
|
||||
);
|
||||
}
|
||||
this.init();
|
||||
}
|
||||
init(){
|
||||
this.listItem.filename.html(
|
||||
this.file.name + " <span class='text-muted'>" + formatBytes(this.file.size) + "</span>"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
$("#files").on('change',function(){
|
||||
for (const file of this.files) {
|
||||
$("#thelist .empty").remove();
|
||||
new FileDescriptor(
|
||||
true,
|
||||
HostListItem(),
|
||||
file
|
||||
)
|
||||
};
|
||||
$("#files").val('');
|
||||
});
|
||||
|
||||
$(".joinroom").on("chnage",function(){
|
||||
|
||||
let ip = $("#remoteadress").val();
|
||||
|
||||
if(!/^\d+\.\d+\.\d+$/.test(ip)){
|
||||
return alert(" ")
|
||||
}
|
||||
|
||||
$("#thelist .empty").remove();
|
||||
|
||||
room = mwse.room({
|
||||
name: `WEB3-Smash [${vip}] RO CONNECT:2`,
|
||||
description: "Web3 Smash saQut Server Application",
|
||||
joinType: "free",
|
||||
notifyActionJoined: true,
|
||||
notifyActionEjected: true,
|
||||
ifexistsJoin: false
|
||||
});
|
||||
})
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
let mwse = new MWSE({
|
||||
endpoint: "ws://localhost:7707"
|
||||
});
|
||||
|
||||
mwse.scope(beginEngine);
|
||||
|
||||
async function beginEngine()
|
||||
{
|
||||
room = mwse.room({
|
||||
name: "Naber",
|
||||
joinType: "free",
|
||||
accessType: "private",
|
||||
description: "Private free joined room",
|
||||
ifexistsJoin: false,
|
||||
notifyActionEjected: true,
|
||||
notifyActionInvite: false,
|
||||
notifyActionJoined: true
|
||||
});
|
||||
try{
|
||||
await room.createRoom();
|
||||
let time = 0;
|
||||
setInterval(()=>{
|
||||
room.info.set("set time",time);
|
||||
time++;
|
||||
},1000);
|
||||
}catch{
|
||||
await room.join();
|
||||
}
|
||||
room.on('updateinfo',(name, value) => {
|
||||
console.log("read",name,value)
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dosya Transfer Sistem</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.7/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" style="max-width: 1200px">
|
||||
|
||||
<h2 class="text-center mb-2 mt-5">Dosya Transfer Sistemi</h2>
|
||||
<h1 class="text-center mb-5 mt-3 text-bold fw-bold">
|
||||
<span>Ağ Adresiniz</span>
|
||||
<span class="text-danger network-id">
|
||||
#.#.#.#
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div class="mx-auto d-flex flex-row mb-5" style="max-width: 800px;gap:20px">
|
||||
<div class="flex-fill">
|
||||
<input
|
||||
type="file"
|
||||
id="files"
|
||||
multiple
|
||||
class="form-control"
|
||||
>
|
||||
</div>
|
||||
<div class="flex-fill d-flex" style="gap:5px">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Farklı bir ağa katıl"
|
||||
id="remoteadress"
|
||||
>
|
||||
<button class="btn btn-outline-success joinroom">
|
||||
Katıl
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-nowrap" width="1%">İşlem</th>
|
||||
<th class="text-nowrap" width="1%">SHA-1</th>
|
||||
<th class="text-nowrap" width="49%">Dosya İsmi</th>
|
||||
<th class="text-nowrap" width="50%">İşlem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="thelist">
|
||||
<tr class="empty">
|
||||
<td class="text-center text-muted" colspan="4">
|
||||
Gönderilecek veya alınacak dosya bulunmuyor
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script src="https://ws.saqut.com/script"></script>
|
||||
<script src="m.h.2.8.8.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,714 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="tr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Network meter</title>
|
||||
</head>
|
||||
<body style="background-color: #333333;">
|
||||
<div id="container">
|
||||
<div class="speed-container">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
html,body,#container{
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
#container{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.speed-container{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 1200px;
|
||||
flex-wrap: wrap;
|
||||
margin: auto;
|
||||
}
|
||||
.speed-container > div{
|
||||
flex: 1 1 25%;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.speed-container > div canvas{
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.speed-container > div .text{
|
||||
display: flex;
|
||||
color: white;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.speed-container > div .mwse{
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
.speed-container > div .text > .text1{
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.speed-container > div .text > .text2{
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.speed-container > div .text1::after{
|
||||
content:' FLOW';
|
||||
font-size: 0.4em;
|
||||
margin: auto 0 auto 5px;
|
||||
}
|
||||
.speed-container > div .text2::after{
|
||||
content:' PRESSURE';
|
||||
font-size: 0.4em;
|
||||
margin: auto 0 auto 5px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function CreateMeter()
|
||||
{
|
||||
var iCurrentSpeed = 0,
|
||||
iTargetSpeed = 0,
|
||||
bDecrement = null,
|
||||
job = null;
|
||||
|
||||
function degToRad(angle) {
|
||||
// Degrees to radians
|
||||
return ((angle * Math.PI) / 180);
|
||||
}
|
||||
|
||||
function radToDeg(angle) {
|
||||
// Radians to degree
|
||||
return ((angle * 180) / Math.PI);
|
||||
}
|
||||
|
||||
function drawLine(options, line) {
|
||||
// Draw a line using the line object passed in
|
||||
options.ctx.beginPath();
|
||||
|
||||
// Set attributes of open
|
||||
options.ctx.globalAlpha = line.alpha;
|
||||
options.ctx.lineWidth = line.lineWidth;
|
||||
options.ctx.fillStyle = line.fillStyle;
|
||||
options.ctx.strokeStyle = line.fillStyle;
|
||||
options.ctx.moveTo(line.from.X,
|
||||
line.from.Y);
|
||||
|
||||
// Plot the line
|
||||
options.ctx.lineTo(
|
||||
line.to.X,
|
||||
line.to.Y
|
||||
);
|
||||
|
||||
options.ctx.stroke();
|
||||
}
|
||||
|
||||
function createLine(fromX, fromY, toX, toY, fillStyle, lineWidth, alpha) {
|
||||
// Create a line object using Javascript object notation
|
||||
return {
|
||||
from: {
|
||||
X: fromX,
|
||||
Y: fromY
|
||||
},
|
||||
to: {
|
||||
X: toX,
|
||||
Y: toY
|
||||
},
|
||||
fillStyle: fillStyle,
|
||||
lineWidth: lineWidth,
|
||||
alpha: alpha
|
||||
};
|
||||
}
|
||||
|
||||
function drawOuterMetallicArc(options) {
|
||||
/* Draw the metallic border of the speedometer
|
||||
* Outer grey area
|
||||
*/
|
||||
options.ctx.beginPath();
|
||||
|
||||
// Nice shade of grey
|
||||
options.ctx.fillStyle = "rgb(127,127,127)";
|
||||
|
||||
// Draw the outer circle
|
||||
options.ctx.arc(options.center.X,
|
||||
options.center.Y,
|
||||
options.radius,
|
||||
0,
|
||||
Math.PI,
|
||||
true);
|
||||
|
||||
// Fill the last object
|
||||
options.ctx.fill();
|
||||
/* */
|
||||
}
|
||||
|
||||
function drawInnerMetallicArc(options) {
|
||||
/* Draw the metallic border of the speedometer
|
||||
* Inner white area
|
||||
*/
|
||||
|
||||
options.ctx.beginPath();
|
||||
|
||||
// White
|
||||
options.ctx.fillStyle = "rgb(255,255,255)";
|
||||
|
||||
// Outer circle (subtle edge in the grey)
|
||||
options.ctx.arc(options.center.X,
|
||||
options.center.Y,
|
||||
(options.radius / 100) * 90,
|
||||
0,
|
||||
Math.PI,
|
||||
true);
|
||||
|
||||
options.ctx.fill();
|
||||
|
||||
/* */
|
||||
}
|
||||
|
||||
function drawMetallicArc(options) {
|
||||
/* Draw the metallic border of the speedometer
|
||||
* by drawing two semi-circles, one over lapping
|
||||
* the other with a bot of alpha transparency
|
||||
*/
|
||||
|
||||
drawOuterMetallicArc(options);
|
||||
drawInnerMetallicArc(options);
|
||||
}
|
||||
|
||||
function drawBackground(options) {
|
||||
/* Black background with alphs transparency to
|
||||
* blend the edges of the metallic edge and
|
||||
* black background
|
||||
*/
|
||||
var i = 0;
|
||||
|
||||
options.ctx.globalAlpha = 0.2;
|
||||
options.ctx.fillStyle = "rgb(0,0,0)";
|
||||
|
||||
// Draw semi-transparent circles
|
||||
for (i = 170; i < 180; i++) {
|
||||
options.ctx.beginPath();
|
||||
|
||||
options.ctx.arc(options.center.X,
|
||||
options.center.Y,
|
||||
i,
|
||||
0,
|
||||
Math.PI,
|
||||
true);
|
||||
|
||||
options.ctx.fill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function applyDefaultContextSettings(options) {
|
||||
/* Helper function to revert to gauges
|
||||
* default settings
|
||||
*/
|
||||
|
||||
options.ctx.lineWidth = 2;
|
||||
options.ctx.globalAlpha = 0.5;
|
||||
options.ctx.strokeStyle = "rgb(255, 255, 255)";
|
||||
options.ctx.fillStyle = 'rgb(255,255,255)';
|
||||
}
|
||||
|
||||
function drawSmallTickMarks(options) {
|
||||
/* The small tick marks against the coloured
|
||||
* arc drawn every 5 mph from 10 degrees to
|
||||
* 170 degrees.
|
||||
*/
|
||||
|
||||
var tickvalue = options.levelRadius - 8,
|
||||
iTick = 0,
|
||||
gaugeOptions = options.gaugeOptions,
|
||||
iTickRad = 0,
|
||||
onArchX,
|
||||
onArchY,
|
||||
innerTickX,
|
||||
innerTickY,
|
||||
fromX,
|
||||
fromY,
|
||||
line,
|
||||
toX,
|
||||
toY;
|
||||
|
||||
applyDefaultContextSettings(options);
|
||||
|
||||
// Tick every 20 degrees (small ticks)
|
||||
for (iTick = 10; iTick < 180; iTick += 10) {
|
||||
|
||||
iTickRad = degToRad(iTick);
|
||||
|
||||
/* Calculate the X and Y of both ends of the
|
||||
* line I need to draw at angle represented at Tick.
|
||||
* The aim is to draw the a line starting on the
|
||||
* coloured arc and continueing towards the outer edge
|
||||
* in the direction from the center of the gauge.
|
||||
*/
|
||||
|
||||
onArchX = gaugeOptions.radius - (Math.cos(iTickRad) * tickvalue);
|
||||
onArchY = gaugeOptions.radius - (Math.sin(iTickRad) * tickvalue);
|
||||
innerTickX = gaugeOptions.radius - (Math.cos(iTickRad) * gaugeOptions.radius);
|
||||
innerTickY = gaugeOptions.radius - (Math.sin(iTickRad) * gaugeOptions.radius);
|
||||
|
||||
fromX = (options.center.X - gaugeOptions.radius) + onArchX;
|
||||
fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + onArchY;
|
||||
toX = (options.center.X - gaugeOptions.radius) + innerTickX;
|
||||
toY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY;
|
||||
|
||||
// Create a line expressed in JSON
|
||||
line = createLine(fromX, fromY, toX, toY, "rgb(127,127,127)", 3, 0.6);
|
||||
|
||||
// Draw the line
|
||||
drawLine(options, line);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function drawLargeTickMarks(options) {
|
||||
/* The large tick marks against the coloured
|
||||
* arc drawn every 10 mph from 10 degrees to
|
||||
* 170 degrees.
|
||||
*/
|
||||
|
||||
var tickvalue = options.levelRadius - 8,
|
||||
iTick = 0,
|
||||
gaugeOptions = options.gaugeOptions,
|
||||
iTickRad = 0,
|
||||
innerTickY,
|
||||
innerTickX,
|
||||
onArchX,
|
||||
onArchY,
|
||||
fromX,
|
||||
fromY,
|
||||
toX,
|
||||
toY,
|
||||
line;
|
||||
|
||||
applyDefaultContextSettings(options);
|
||||
|
||||
tickvalue = options.levelRadius - 2;
|
||||
|
||||
// 10 units (major ticks)
|
||||
for (iTick = 20; iTick < 180; iTick += 20) {
|
||||
|
||||
iTickRad = degToRad(iTick);
|
||||
|
||||
/* Calculate the X and Y of both ends of the
|
||||
* line I need to draw at angle represented at Tick.
|
||||
* The aim is to draw the a line starting on the
|
||||
* coloured arc and continueing towards the outer edge
|
||||
* in the direction from the center of the gauge.
|
||||
*/
|
||||
|
||||
onArchX = gaugeOptions.radius - (Math.cos(iTickRad) * tickvalue);
|
||||
onArchY = gaugeOptions.radius - (Math.sin(iTickRad) * tickvalue);
|
||||
innerTickX = gaugeOptions.radius - (Math.cos(iTickRad) * gaugeOptions.radius);
|
||||
innerTickY = gaugeOptions.radius - (Math.sin(iTickRad) * gaugeOptions.radius);
|
||||
|
||||
fromX = (options.center.X - gaugeOptions.radius) + onArchX;
|
||||
fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + onArchY;
|
||||
toX = (options.center.X - gaugeOptions.radius) + innerTickX;
|
||||
toY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY;
|
||||
|
||||
// Create a line expressed in JSON
|
||||
line = createLine(fromX, fromY, toX, toY, "rgb(127,127,127)", 3, 0.6);
|
||||
|
||||
// Draw the line
|
||||
drawLine(options, line);
|
||||
}
|
||||
}
|
||||
|
||||
function drawTicks(options) {
|
||||
/* Two tick in the coloured arc!
|
||||
* Small ticks every 5
|
||||
* Large ticks every 10
|
||||
*/
|
||||
drawSmallTickMarks(options);
|
||||
drawLargeTickMarks(options);
|
||||
}
|
||||
|
||||
function drawTextMarkers(options) {
|
||||
/* The text labels marks above the coloured
|
||||
* arc drawn every 10 mph from 10 degrees to
|
||||
* 170 degrees.
|
||||
*/
|
||||
var innerTickX = 0,
|
||||
innerTickY = 0,
|
||||
iTick = 0,
|
||||
gaugeOptions = options.gaugeOptions,
|
||||
iTickToPrint = 00;
|
||||
|
||||
applyDefaultContextSettings(options);
|
||||
|
||||
// Font styling
|
||||
options.ctx.font = 'italic 10px sans-serif';
|
||||
options.ctx.textBaseline = 'top';
|
||||
|
||||
options.ctx.beginPath();
|
||||
|
||||
// Tick every 20 (small ticks)
|
||||
for (iTick = 10; iTick < 160; iTick += 20) {
|
||||
|
||||
innerTickX = gaugeOptions.radius - (Math.cos(degToRad(iTick)) * gaugeOptions.radius);
|
||||
innerTickY = gaugeOptions.radius - (Math.sin(degToRad(iTick)) * gaugeOptions.radius);
|
||||
|
||||
// Some cludging to center the values (TODO: Improve)
|
||||
if (iTick <= 10) {
|
||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX,
|
||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
|
||||
} else if (iTick < 50) {
|
||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX - 5,
|
||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
|
||||
} else if (iTick < 90) {
|
||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX,
|
||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
|
||||
} else if (iTick === 90) {
|
||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 4,
|
||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
|
||||
} else if (iTick < 145) {
|
||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 10,
|
||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
|
||||
} else {
|
||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 15,
|
||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
|
||||
}
|
||||
|
||||
// MPH increase by 10 every 20 degrees
|
||||
//iTickToPrint += Math.round(2160 / 9);
|
||||
iTickToPrint += 30;
|
||||
}
|
||||
|
||||
options.ctx.stroke();
|
||||
}
|
||||
|
||||
function drawSpeedometerPart(options, alphaValue, strokeStyle, startPos) {
|
||||
/* Draw part of the arc that represents
|
||||
* the colour speedometer arc
|
||||
*/
|
||||
|
||||
options.ctx.beginPath();
|
||||
|
||||
options.ctx.globalAlpha = alphaValue;
|
||||
options.ctx.lineWidth = 5;
|
||||
options.ctx.strokeStyle = strokeStyle;
|
||||
|
||||
options.ctx.arc(options.center.X,
|
||||
options.center.Y,
|
||||
options.levelRadius,
|
||||
Math.PI + (Math.PI / 360 * startPos),
|
||||
0 - (Math.PI / 360 * 10),
|
||||
false);
|
||||
|
||||
options.ctx.stroke();
|
||||
}
|
||||
|
||||
function drawSpeedometerColourArc(options) {
|
||||
/* Draws the colour arc. Three different colours
|
||||
* used here; thus, same arc drawn 3 times with
|
||||
* different colours.
|
||||
* TODO: Gradient possible?
|
||||
*/
|
||||
|
||||
|
||||
drawSpeedometerPart(options, 1.0, "rgb(255,255,255)", 10);
|
||||
drawSpeedometerPart(options, 0.9, "rgb(0,255,0)", 80);
|
||||
drawSpeedometerPart(options, 0.9, "rgb(255,128,0)", 250);
|
||||
drawSpeedometerPart(options, 0.9, "rgb(255,0,0)", 300);
|
||||
|
||||
}
|
||||
|
||||
function drawNeedleDial(options, alphaValue, strokeStyle, fillStyle) {
|
||||
/* Draws the metallic dial that covers the base of the
|
||||
* needle.
|
||||
*/
|
||||
var i = 0;
|
||||
|
||||
options.ctx.globalAlpha = alphaValue;
|
||||
options.ctx.lineWidth = 3;
|
||||
options.ctx.strokeStyle = strokeStyle;
|
||||
options.ctx.fillStyle = fillStyle;
|
||||
|
||||
// Draw several transparent circles with alpha
|
||||
for (i = 0; i < 30; i++) {
|
||||
|
||||
options.ctx.beginPath();
|
||||
options.ctx.arc(options.center.X,
|
||||
options.center.Y,
|
||||
i,
|
||||
0,
|
||||
Math.PI,
|
||||
true);
|
||||
|
||||
options.ctx.fill();
|
||||
options.ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function drawNeedle(options) {
|
||||
/* Draw the needle in a nice read colour at the
|
||||
* angle that represents the options.speed value.
|
||||
*/
|
||||
|
||||
var iSpeedAsAngle = convertSpeedToAngle(options),
|
||||
iSpeedAsAngleRad = degToRad(iSpeedAsAngle),
|
||||
gaugeOptions = options.gaugeOptions,
|
||||
innerTickX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * 20),
|
||||
innerTickY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * 20),
|
||||
fromX = (options.center.X - gaugeOptions.radius) + innerTickX,
|
||||
fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY,
|
||||
endNeedleX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * gaugeOptions.radius),
|
||||
endNeedleY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * gaugeOptions.radius),
|
||||
toX = (options.center.X - gaugeOptions.radius) + endNeedleX,
|
||||
toY = (gaugeOptions.center.Y - gaugeOptions.radius) + endNeedleY,
|
||||
line = createLine(fromX, fromY, toX, toY, "rgb(255, 0, 0)", 5, 0.6);
|
||||
|
||||
drawLine(options, line);
|
||||
|
||||
// Two circle to draw the dial at the base (give its a nice effect?)
|
||||
drawNeedleDial(options, 0.6, "rgb(127, 127, 127)", "rgb(255,255,255)");
|
||||
drawNeedleDial(options, 0.2, "rgb(127, 127, 127)", "rgb(127,127,127)");
|
||||
|
||||
}
|
||||
|
||||
function buildOptionsAsJSON(canvas, iSpeed) {
|
||||
/* Setting for the speedometer
|
||||
* Alter these to modify its look and feel
|
||||
*/
|
||||
|
||||
var centerX = 210,
|
||||
centerY = 210,
|
||||
radius = 150,
|
||||
outerRadius = 200;
|
||||
|
||||
// Create a speedometer object using Javascript object notation
|
||||
return {
|
||||
ctx: canvas.getContext('2d'),
|
||||
speed: iSpeed,
|
||||
center: {
|
||||
X: centerX,
|
||||
Y: centerY
|
||||
},
|
||||
levelRadius: radius - 10,
|
||||
gaugeOptions: {
|
||||
center: {
|
||||
X: centerX,
|
||||
Y: centerY
|
||||
},
|
||||
radius: radius
|
||||
},
|
||||
radius: outerRadius
|
||||
};
|
||||
}
|
||||
|
||||
function clearCanvas(options) {
|
||||
options.ctx.clearRect(0, 0, 800, 600);
|
||||
applyDefaultContextSettings(options);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
/* Main entry point for drawing the speedometer
|
||||
* If canvas is not support alert the user.
|
||||
*/
|
||||
var canvas = createCanvas.value,
|
||||
options = null;
|
||||
|
||||
// Canvas good?
|
||||
if (canvas !== null && canvas.getContext) {
|
||||
options = buildOptionsAsJSON(canvas, iCurrentSpeed);
|
||||
|
||||
// Clear canvas
|
||||
clearCanvas(options);
|
||||
|
||||
// Draw the metallic styled edge
|
||||
drawMetallicArc(options);
|
||||
|
||||
// Draw thw background
|
||||
drawBackground(options);
|
||||
|
||||
// Draw tick marks
|
||||
drawTicks(options);
|
||||
|
||||
// Draw labels on markers
|
||||
drawTextMarkers(options);
|
||||
|
||||
// Draw speeometer colour arc
|
||||
drawSpeedometerColourArc(options);
|
||||
|
||||
// Draw the needle and base
|
||||
drawNeedle(options);
|
||||
|
||||
} else {
|
||||
alert("Canvas not supported by your browser!");
|
||||
};
|
||||
|
||||
if(iTargetSpeed == iCurrentSpeed) {
|
||||
cancelAnimationFrame(job);
|
||||
return;
|
||||
} else if(iTargetSpeed < iCurrentSpeed) {
|
||||
bDecrement = true;
|
||||
} else if(iTargetSpeed > iCurrentSpeed) {
|
||||
bDecrement = false;
|
||||
}
|
||||
|
||||
console.log(iTargetSpeed,bDecrement)
|
||||
|
||||
if(bDecrement) {
|
||||
iCurrentSpeed = iCurrentSpeed - 1;
|
||||
} else {
|
||||
iCurrentSpeed = iCurrentSpeed + 1;
|
||||
}
|
||||
|
||||
job = requestAnimationFrame(draw);
|
||||
};
|
||||
function createCanvas()
|
||||
{
|
||||
let container = document.createElement("div");
|
||||
container.classList.add("boxmodel")
|
||||
let canvas = document.createElement("canvas");
|
||||
|
||||
let text = document.createElement("div");
|
||||
let text1 = document.createElement("div");
|
||||
let text2 = document.createElement("div");
|
||||
text.classList.add("text")
|
||||
text1.classList.add("text1")
|
||||
text2.classList.add("text2")
|
||||
|
||||
let mwse = document.createElement("div");
|
||||
let text3 = document.createElement("span");
|
||||
let text4 = document.createElement("span");
|
||||
mwse.classList.add("mwse")
|
||||
text3.classList.add("text3")
|
||||
text4.classList.add("text4")
|
||||
|
||||
text.append(text1);
|
||||
text.append(text2);
|
||||
|
||||
mwse.append(text3);
|
||||
mwse.append(text4);
|
||||
|
||||
canvas.setAttribute("width", "440px");
|
||||
canvas.setAttribute("height", "220px");
|
||||
container.append(canvas);
|
||||
container.append(text);
|
||||
container.append(mwse);
|
||||
createCanvas.value = canvas;
|
||||
createCanvas.container = container;
|
||||
createCanvas.ps = text1;
|
||||
createCanvas.pw = text2;
|
||||
createCanvas.rooms = text3;
|
||||
createCanvas.clients = text4;
|
||||
return container;
|
||||
};
|
||||
function update(value, pressure, rooms, clients)
|
||||
{
|
||||
iTargetSpeed = value;
|
||||
createCanvas.ps.innerText = value;
|
||||
createCanvas.pw.innerText = pressure + '%';
|
||||
createCanvas.rooms.innerText = rooms;
|
||||
createCanvas.clients.innerText = clients;
|
||||
draw()
|
||||
}
|
||||
function convertSpeedToAngle(options) {
|
||||
/* Helper function to convert a speed to the
|
||||
* equivelant angle.
|
||||
*/
|
||||
let m = 150;
|
||||
let value = (m/10) + Math.min(
|
||||
Math.max(
|
||||
options.speed,
|
||||
0
|
||||
),
|
||||
240
|
||||
);
|
||||
iSpeedAsAngle = (
|
||||
(100 / m) * value
|
||||
);
|
||||
|
||||
// Ensure the angle is within range
|
||||
if (iSpeedAsAngle > 180) {
|
||||
iSpeedAsAngle = iSpeedAsAngle - 180;
|
||||
} else if (iSpeedAsAngle < 0) {
|
||||
iSpeedAsAngle = iSpeedAsAngle + 180;
|
||||
}
|
||||
|
||||
return iSpeedAsAngle;
|
||||
}
|
||||
return {
|
||||
createCanvas,
|
||||
update
|
||||
}
|
||||
}
|
||||
/*
|
||||
let elem = document.querySelector(".speed-container");
|
||||
for(let e = 0; e < 8; e++)
|
||||
{
|
||||
let meter = CreateMeter();
|
||||
let container = meter.createCanvas();
|
||||
elem.appendChild(container);
|
||||
meter.update(54,25)
|
||||
}
|
||||
*/
|
||||
let elem = document.querySelector(".speed-container");
|
||||
let meters = new Map();
|
||||
async function reloadData()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
await fetchData();
|
||||
await new Promise(ok => {
|
||||
setTimeout(() => ok(), 3000)
|
||||
})
|
||||
}
|
||||
};
|
||||
let isFirst = true;
|
||||
async function fetchData()
|
||||
{
|
||||
let response = await fetch("/stats",{
|
||||
method: "post",
|
||||
credentials: "same-origin",
|
||||
cache: "no-cache",
|
||||
mode:"no-cors"
|
||||
}).then(e => e.json());
|
||||
for (const {
|
||||
ws_total_packs,
|
||||
core,
|
||||
mwse_rooms,
|
||||
mwse_clients
|
||||
} of response) {
|
||||
if(!meters.has(core))
|
||||
{
|
||||
let meter = CreateMeter();
|
||||
let container = meter.createCanvas();
|
||||
elem.appendChild(container);
|
||||
meters.set(core, meter)
|
||||
};
|
||||
let _meter = meters.get(core);
|
||||
_meter.update(
|
||||
ws_total_packs,
|
||||
ws_total_packs < 30 ? 0 : ws_total_packs < 60 ? 1 : ws_total_packs < 90 ? 2 : 3,
|
||||
"RM: "+mwse_rooms,
|
||||
"CL: "+mwse_clients
|
||||
);
|
||||
}
|
||||
}
|
||||
reloadData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1579
script/index.js
1579
script/index.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue