github.com/attic-labs/noms@v0.0.0-20210827224422-e5fa29d95e8b/samples/go/decent/p2p-chat/main.go (about)

     1  // See: https://github.com/attic-labs/noms/issues/3808
     2  // +build ignore
     3  
     4  // Copyright 2017 Attic Labs, Inc. All rights reserved.
     5  // Licensed under the Apache License, version 2.0:
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  
     8  package main
     9  
    10  import (
    11  	"fmt"
    12  	"log"
    13  	"net"
    14  	"os"
    15  	"os/signal"
    16  	"path"
    17  	"syscall"
    18  
    19  	"github.com/attic-labs/noms/go/config"
    20  	"github.com/attic-labs/noms/go/d"
    21  	"github.com/attic-labs/noms/go/datas"
    22  	"github.com/attic-labs/noms/go/ipfs"
    23  	"github.com/attic-labs/noms/go/spec"
    24  	"github.com/attic-labs/noms/go/util/profile"
    25  	"github.com/attic-labs/noms/samples/go/decent/dbg"
    26  	"github.com/attic-labs/noms/samples/go/decent/lib"
    27  	"github.com/jroimartin/gocui"
    28  	kingpin "gopkg.in/alecthomas/kingpin.v2"
    29  )
    30  
    31  func main() {
    32  	// allow short (-h) help
    33  	kingpin.CommandLine.HelpFlag.Short('h')
    34  
    35  	clientCmd := kingpin.Command("client", "runs the ipfs-chat client UI")
    36  	clientTopic := clientCmd.Flag("topic", "IPFS pubsub topic to publish and subscribe to").Default("noms-chat-p2p").String()
    37  	username := clientCmd.Flag("username", "username to sign in as").Required().String()
    38  	nodeIdx := clientCmd.Flag("node-idx", "a single digit to be used as last digit in all port values: api, gateway and swarm (must be 0-9 inclusive)").Default("-1").Int()
    39  	clientDir := clientCmd.Arg("path", "local directory to store data in").Required().ExistingDir()
    40  
    41  	importCmd := kingpin.Command("import", "imports data into a chat")
    42  	importSrc := importCmd.Flag("dir", "directory that contains data to import").Default("../data").ExistingDir()
    43  	importDir := importCmd.Arg("path", "local directory to store data in").Required().ExistingDir()
    44  
    45  	kingpin.CommandLine.Help = "A demonstration of using Noms to build a scalable multiuser collaborative application."
    46  
    47  	switch kingpin.Parse() {
    48  	case "client":
    49  		cInfo := lib.ClientInfo{
    50  			Topic:    *clientTopic,
    51  			Username: *username,
    52  			Idx:      *nodeIdx,
    53  			IsDaemon: false,
    54  			Dir:      *clientDir,
    55  			Delegate: lib.P2PEventDelegate{},
    56  		}
    57  		runClient(cInfo)
    58  	case "import":
    59  		err := lib.RunImport(*importSrc, fmt.Sprintf("%s/noms::chat", *importDir))
    60  		d.PanicIfError(err)
    61  	}
    62  }
    63  
    64  func runClient(cInfo lib.ClientInfo) {
    65  	dbg.SetLogger(lib.NewLogger(cInfo.Username))
    66  
    67  	var err error
    68  	httpPort := 8000 + cInfo.Idx
    69  	sp, err := spec.ForDatabase(fmt.Sprintf("http://%s:%d", getIP(), httpPort))
    70  	d.PanicIfError(err)
    71  	cInfo.Spec = sp
    72  
    73  	<-runServer(path.Join(cInfo.Dir, "noms"), httpPort)
    74  
    75  	db := cInfo.Spec.GetDatabase()
    76  	ds := db.GetDataset("chat")
    77  	ds, err = lib.InitDatabase(ds)
    78  	d.PanicIfError(err)
    79  
    80  	node := ipfs.OpenIPFSRepo(path.Join(cInfo.Dir, "ipfs"), cInfo.Idx)
    81  	events := make(chan lib.ChatEvent, 1024)
    82  	t := lib.CreateTermUI(events)
    83  	defer t.Close()
    84  
    85  	d.PanicIfError(t.Layout())
    86  	t.ResetAuthors(ds)
    87  	t.UpdateMessages(ds, nil, nil)
    88  
    89  	go lib.ProcessChatEvents(node, ds, events, t, cInfo)
    90  	go lib.ReceiveMessages(node, events, cInfo)
    91  
    92  	if err := t.Gui.MainLoop(); err != nil && err != gocui.ErrQuit {
    93  		dbg.Debug("mainloop has exited, err:", err)
    94  		log.Panicln(err)
    95  	}
    96  }
    97  
    98  func getIP() string {
    99  	ifaces, err := net.Interfaces()
   100  	d.PanicIfError(err)
   101  	for _, i := range ifaces {
   102  		addrs, err := i.Addrs()
   103  		d.PanicIfError(err)
   104  		for _, addr := range addrs {
   105  			switch v := addr.(type) {
   106  			case *net.IPNet:
   107  				if !v.IP.IsLoopback() {
   108  					ip := v.IP.To4()
   109  					if ip != nil {
   110  						return v.IP.String()
   111  					}
   112  				}
   113  			}
   114  		}
   115  	}
   116  	d.Panic("notreached")
   117  	return ""
   118  }
   119  
   120  func runServer(atPath string, port int) (ready chan struct{}) {
   121  	ready = make(chan struct{})
   122  	_ = os.Mkdir(atPath, 0755)
   123  	cfg := config.NewResolver()
   124  	cs, err := cfg.GetChunkStore(atPath)
   125  	d.CheckError(err)
   126  	server := datas.NewRemoteDatabaseServer(cs, port)
   127  	server.Ready = func() {
   128  		ready <- struct{}{}
   129  	}
   130  
   131  	// Shutdown server gracefully so that profile may be written
   132  	c := make(chan os.Signal, 1)
   133  	signal.Notify(c, os.Interrupt)
   134  	signal.Notify(c, syscall.SIGTERM)
   135  	go func() {
   136  		<-c
   137  		server.Stop()
   138  	}()
   139  
   140  	go func() {
   141  		d.Try(func() {
   142  			defer profile.MaybeStartProfile().Stop()
   143  			server.Run()
   144  		})
   145  	}()
   146  	return
   147  }