Skip to content

Commit

Permalink
[JS-SOCKET]: Remove old implementation and notification implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
amadolid committed Jan 4, 2024
1 parent de38d75 commit 8c8c4c7
Show file tree
Hide file tree
Showing 27 changed files with 818 additions and 353 deletions.
227 changes: 227 additions & 0 deletions docs/docs/docs/deployment/extension_services/socket.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
---
title: Socket
---

# **HOW TO SETUP `SOCKET_SVC`**
Socket service is disabled by default. To enable it, you need to update `SOCKET_CONFIG`.
```python
{
"enabled": True,
"quiet": False,
"automated": False,
"url": os.getenv("SOCKET_URL", "ws://jaseci-socket/ws"),
"ping_url": os.getenv("SOCKET_PING_URL", "http://jaseci-socket/healthz"),
"auth": os.getenv("SOCKET_AUTH", "12345678"),
}
```
`automated` is disabled by default. Enabling it will allow JsOrc to regenerate it using `SOCKET_MANIFEST`, similar to *redis*, *elastic* and *prometheus*.

You may run your own websocket. However, you may need to adjust it to cover current Socket service approach.

----

# **`JSSOCKET`**
By default, Socket service uses jssocket as websocket server.

## HOW TO INSTALL
```bash
git clone https://github.com/Jaseci-Labs/jaseci.git
cd jaseci/jaseci_socket
. install.sh
```

## HOW TO SETUP
you may add your preferred authentication by adding environment variable `SOCKET_AUTH`. It must be on the same format as the return from python's `bcrypt.hashpw`. `SOCKET_AUTH` defaults to `1234678` in raw string.
```python
bcrypt.hashpw(
"your raw password".encode("utf-8"),
bcrypt.gensalt()
).hex()
```

## HOW TO RUN
```bash
# ----------- default ----------- #
jssocket
# server listening on 0.0.0.0:80
# ------------------------------- #


# -------- preferred port -------- #
jssocket -p 81
jssocket --port 81
# server listening on 0.0.0.0:81
# -------------------------------- #

# -------- preferred host -------- #
jssocket -h localhost
jssocket --host localhost
# server listening on 127.0.0.1:80
# -------------------------------- #

# --- preferred host and port ---- #
jssocket -h localhost --port 81
# server listening on 127.0.0.1:81
# -------------------------------- #
```

## HOW TO USE
#### EVENT STRUCTURE
```js
{
"type": "YOUR EVENT TYPE",
"data": { /* your payload */ }
}
```
#### EVENT TYPE
- `server_connect`
- event used only by jaseci to start up socket service
- this will authenticate jaseci's socket service. Defaults to 12345678
- will automatically disconnect when authentication fails
- OUT EVENT:
```js
{
"type": "server_connect",
"data": {
"auth": "{{SOCKET_CONFIG'S auth field}}"
}
}
```
- IN EVENT:
```js
{
"type": "server_connect",
"data": true, // false when authentication fails
}
```
- `client_connect`
- event used by clients to connect to jssocket
- OUT EVENT:
```js
{
"type": "client_connect",
"data": {
"token": "{{current user token}}" // optional
}
}
```
- IN EVENT:
```js
{
"type": "client_connected",
"data": {
"target": "{{current connection id}}"
"authenticated": true, // else false for any reason for not being authenticated
}
}
```
- `client_connected`
- only server client (jaseci) can send this
- this is the event triggered by server client (jaseci) in response to client_connect
- `client_disconnect`
- disconnect to websocket
- `notify_client`
- notify client with any serializable data
- OUT EVENT:
```js
{
"type": "notify_client",
"data": {
"target": "{{other's connection id}}", // optional: default to self connection id
"data": {/* your event data */}
}
}
```
- IN EVENT:
```js
{/* your event data */}
```
- `notify_group`
- notify group of clients with any serializable data
- authenticated user will be included to their master id group
- non authenticated user will be included to public group
- OUT EVENT:
```js
{
"type": "notify_group",
"data": {
"target": "{{other's connection id or group id}}", // optional: default to self connection id
// target will check it's current group if connection id is used and this will used as the actual target
"data": {/* your event data */}
}
}
```
- IN EVENT:
```js
{/* your event data */}
```
- `notify_all`
- only server client (jaseci) can send this
- notify all clients with any serializable data
- OUT EVENT:
```js
{
"type": "notify_all",
"data": {/* your event data */}
}
```
- IN EVENT:
```js
{/* your event data */}
```

## SOCKET SERVICE INTEGRATION
### **`NOTIFICATION FROM JAC`**
#### ws.**`notify_client`**
> **`Arguments`:** \
> **target**: str \
> **data**: dict
>
> **`Return`:** \
> None
>
> **`Usage`:** \
> Send notification from jac to target channel
>
> **`Remarks`:** \
> target can be any connnection id \
> else current connection id is used
##### **`HOW TO TRIGGER`**
```js
ws.notify_client(target, {"test": 123456});
```
---
#### ws.**`notify_group`**
> **`Arguments`:** \
> **target**: str \
> **data**: dict
>
> **`Return`:** \
> None
>
> **`Usage`:** \
> Send notification from jac to target group
>
> **`Remarks`:** \
> target can be any connnection id or user's master id without urn:uuid
> use public if you want to notify all non authenticated user
##### **`HOW TO TRIGGER`**
```js
ws.notify_group(target, {"test": 123456});
```
---
#### ws.**`notify_all`**
> **`Arguments`:** \
> **data**: dict
>
> **`Return`:** \
> None
>
> **`Usage`:** \
> Send notification from jac to all clients
>
##### **`HOW TO TRIGGER`**
```js
ws.notify_all({"test": 123456});
```
1 change: 1 addition & 0 deletions jaseci_core/jaseci/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def load_standard():
import jaseci.extens.act_lib.stripe # noqa
import jaseci.extens.act_lib.regex # noqa
import jaseci.extens.act_lib.maths # noqa
import jaseci.extens.act_lib.socket # noqa


load_standard()
30 changes: 30 additions & 0 deletions jaseci_core/jaseci/extens/act_lib/socket.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from jaseci.jsorc.live_actions import jaseci_action
from jaseci.jsorc.jsorc import JsOrc
from jaseci.extens.svc.socket_svc import SocketService
from jaseci.utils.utils import master_from_meta, logger


def get():
return JsOrc.svc("socket").poke(SocketService)


@jaseci_action(act_group=["ws"])
def notify_client(target: str, data: dict):
get().notify("client", target, data)


@jaseci_action(act_group=["ws"])
def notify_group(target: str, data: dict):
get().notify("group", target, data)


@jaseci_action(act_group=["ws"])
def notify_all(data: dict, meta: dict = {}):
ss = get()

mast = master_from_meta(meta)
if not mast.is_master(super_check=True, silent=False):
logger.error("You don't have permission to notify all clients!")
return

ss.send(ss.app, {"type": f"notify_all", "data": data})
3 changes: 3 additions & 0 deletions jaseci_core/jaseci/extens/svc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .socket_svc import SocketService

__all__ = ["SocketService"]
115 changes: 115 additions & 0 deletions jaseci_core/jaseci/extens/svc/socket_svc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import rel
import websocket
from websocket import WebSocketApp as wsa
from jaseci.jsorc.jsorc import JsOrc
from jaseci.utils.utils import logger
from orjson import loads, dumps
from requests import get


@JsOrc.service(
name="socket",
config="SOCKET_CONFIG",
manifest="SOCKET_MANIFEST",
priority=0,
pre_loaded=True,
)
class SocketService(JsOrc.CommonService):
def run(self):
self.method = {
"server_connect": self.server_connect,
"client_connect": self.client_connect,
}
self.prefix = self.config.get("prefix") or "jws-"
self.app = websocket.WebSocketApp(
self.config.get("url"),
on_open=self.on_open,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
)
self.ping()

self.app.run_forever(dispatcher=rel, reconnect=5)
rel.signal(2, rel.abort)

def post_run(self):
self.spawn_daemon(socket=rel.dispatch)

def ping(self):
get(self.config.get("ping_url")).raise_for_status()

def send(self, ws: wsa, data: dict):
try:
ws.send(dumps(data))
except Exception:
logger.exception("Failed to send event!")

###################################################
# METHODS #
###################################################

def server_connect(self, ws: wsa, data: dict):
if not self.quiet:
logger.info(data)

def client_connect(self, ws: wsa, data: dict):
data["user"] = "public"
self.send(ws, {"type": "client_connected", "data": data})

###################################################
# ACTIONS #
###################################################

def notify(self, type: str, target: str, data: dict):
self.send(
self.app,
{"type": f"notify_{type}", "data": {"target": target, "data": data}},
)

###################################################
# EVENTS #
###################################################

def on_message(self, ws: wsa, event):
event: dict = loads(event)
if not self.quiet:
logger.info(event)

method = self.method.get(event["type"])
if method:
method(ws, event.get("data") or {})

def on_error(self, ws: wsa, error):
self.failed(Exception(error))

def on_close(self, ws: wsa, close_status_code, close_msg):
if not self.quiet:
msg = f"Socket connection closed!\n{close_msg}"
logger.info(msg)
self.failed(Exception(msg))

def on_open(self, ws: wsa):
self.send(
ws,
{"type": "server_connect", "data": {"auth": self.config.get("auth")}},
)

###################################################
# CLEANER #
###################################################

def failed(self, error):
super().failed(error)
self.terminate_daemon("socket")

# ---------------- PROXY EVENTS ----------------- #

def on_delete(self):
try:
if self.is_running():
self.app.close()

self.terminate_daemon("socket")
except Exception:
pass
Loading

0 comments on commit 8c8c4c7

Please sign in to comment.