Skip to content

Commit

Permalink
make build-ui changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mrpalide committed Jun 20, 2022
1 parent afed86e commit 717d3ca
Show file tree
Hide file tree
Showing 9 changed files with 1,034 additions and 3 deletions.
52 changes: 52 additions & 0 deletions cmd/apps/skydex/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Skywire Chat app

Chat implements basic text messaging between skywire visors.

Messaging UI is exposed via web interface.

Chat only supports one WEB client user at a time.

## Local setup

Create 2 visor config files:

`skywire1.json`

```json
{
"apps": [
{
"app": "skychat",
"version": "1.0",
"auto_start": true,
"port": 1
}
]
}
```

`skywire2.json`

```json
{
"apps": [
{
"app": "skychat",
"version": "1.0",
"auto_start": true,
"port": 1,
"args": ["-addr", ":8002"]
}
]
}
```

Compile binaries and start 2 visors:

```bash
$ go build -o apps/skychat.v1.0 ./cmd/apps/skychat
$ ./skywire-visor skywire1.json
$ ./skywire-visor skywire2.json
```

Chat interface will be available on ports `8001` and `8002`.
233 changes: 233 additions & 0 deletions cmd/apps/skydex/skydex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
skydex app for skywire visor
*/
package main

import (
"context"
"embed"
"encoding/json"
"flag"
"fmt"
"io/fs"
"net"
"net/http"
"os"
"sync"
"time"

ipc "github.com/james-barrow/golang-ipc"
"github.com/skycoin/skycoin/src/util/logging"

"github.com/skycoin/skywire-utilities/pkg/cipher"
"github.com/skycoin/skywire-utilities/pkg/netutil"
"github.com/skycoin/skywire/pkg/app"
"github.com/skycoin/skywire/pkg/app/appnet"
"github.com/skycoin/skywire/pkg/app/launcher"
"github.com/skycoin/skywire/pkg/routing"
"github.com/skycoin/skywire/pkg/skyenv"
)

const (
netType = appnet.TypeSkynet
port = routing.Port(1)
)

var log = logging.MustGetLogger("chat")
var addr = flag.String("addr", ":8585", "address to bind")
var r = netutil.NewRetrier(log, 50*time.Millisecond, netutil.DefaultMaxBackoff, 5, 2)

var (
appCl *app.Client
clientCh chan string
conns map[cipher.PubKey]net.Conn // Chat connections
connsMu sync.Mutex
)

// the go embed static points to skywire/cmd/apps/skychat/static

//go:embed static
var embededFiles embed.FS

func main() {
appCl = app.NewClient(nil)
defer appCl.Close()

fmt.Println(appCl.Config().VisorPK)

err := http.ListenAndServe(*addr, nil)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func listenLoop() {
l, err := appCl.Listen(netType, port)
if err != nil {
fmt.Printf("Error listening network %v on port %d: %v\n", netType, port, err)
return
}

for {
fmt.Print("Accepting skychat conn...")
conn, err := l.Accept()
if err != nil {
fmt.Print("Failed to accept conn:", err)
return
}
fmt.Print("Accepted skychat conn")

raddr := conn.RemoteAddr().(appnet.Addr)
connsMu.Lock()
conns[raddr.PubKey] = conn
connsMu.Unlock()
fmt.Printf("Accepted skychat conn on %s from %s\n", conn.LocalAddr(), raddr.PubKey)

go handleConn(conn)
}
}

func handleConn(conn net.Conn) {
raddr := conn.RemoteAddr().(appnet.Addr)
for {
buf := make([]byte, 32*1024)
n, err := conn.Read(buf)
if err != nil {
fmt.Print("Failed to read packet:", err)
raddr := conn.RemoteAddr().(appnet.Addr)
connsMu.Lock()
delete(conns, raddr.PubKey)
connsMu.Unlock()
return
}

clientMsg, err := json.Marshal(map[string]string{"sender": raddr.PubKey.Hex(), "message": string(buf[:n])})
if err != nil {
fmt.Printf("Failed to marshal json: %v", err)
}
select {
case clientCh <- string(clientMsg):
fmt.Printf("Received and sent to ui: %s\n", clientMsg)
default:
fmt.Printf("Received and trashed: %s\n", clientMsg)
}
}
}

func messageHandler(ctx context.Context) func(w http.ResponseWriter, rreq *http.Request) {
return func(w http.ResponseWriter, req *http.Request) {

data := map[string]string{}
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

pk := cipher.PubKey{}
if err := pk.UnmarshalText([]byte(data["recipient"])); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

addr := appnet.Addr{
Net: netType,
PubKey: pk,
Port: 1,
}
connsMu.Lock()
conn, ok := conns[pk]
connsMu.Unlock()

if !ok {
var err error
err = r.Do(ctx, func() error {
conn, err = appCl.Dial(addr)
return err
})
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

connsMu.Lock()
conns[pk] = conn
connsMu.Unlock()

go handleConn(conn)
}

_, err := conn.Write([]byte(data["message"]))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)

connsMu.Lock()
delete(conns, pk)
connsMu.Unlock()

return
}
}
}

func sseHandler(w http.ResponseWriter, req *http.Request) {
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported!", http.StatusBadRequest)
return
}

w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Transfer-Encoding", "chunked")

for {
select {
case msg, ok := <-clientCh:
if !ok {
return
}
_, _ = fmt.Fprintf(w, "data: %s\n\n", msg)
f.Flush()

case <-req.Context().Done():
fmt.Print("SSE connection were closed.")
return
}
}
}

func getFileSystem() http.FileSystem {
fsys, err := fs.Sub(embededFiles, "static")
if err != nil {
panic(err)
}
return http.FS(fsys)
}

func handleIPCSignal(client *ipc.Client) {
for {
m, err := client.Read()
if err != nil {
fmt.Printf("%s IPC received error: %v", skyenv.SkychatName, err)
}
if m.MsgType == skyenv.IPCShutdownMessageType {
fmt.Println("Stopping " + skyenv.SkychatName + " via IPC")
break
}
}
os.Exit(0)
}

func setAppStatus(appCl *app.Client, status launcher.AppDetailedStatus) {
if err := appCl.SetDetailedStatus(string(status)); err != nil {
fmt.Printf("Failed to set status %v: %v\n", status, err)
}
}

func setAppError(appCl *app.Client, appErr error) {
if err := appCl.SetError(appErr.Error()); err != nil {
fmt.Printf("Failed to set error %v: %v\n", appErr, err)
}
}
Loading

0 comments on commit 717d3ca

Please sign in to comment.