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 }