github.com/swishcloud/filesync@v0.0.0-20231002120458-6ade2feed6f9/server/server.go (about) 1 package server 2 3 import ( 4 "crypto/tls" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "net" 11 "net/http" 12 "net/url" 13 "os" 14 "strconv" 15 16 "github.com/google/uuid" 17 "github.com/swishcloud/filesync/internal" 18 "github.com/swishcloud/filesync/storage" 19 20 "golang.org/x/oauth2" 21 "gopkg.in/yaml.v2" 22 23 "github.com/swishcloud/filesync/message" 24 "github.com/swishcloud/filesync/message/models" 25 "github.com/swishcloud/filesync/session" 26 "github.com/swishcloud/filesync/x" 27 "github.com/swishcloud/gostudy/common" 28 ) 29 30 type FileSyncServer struct { 31 config *config 32 skip_tls_verify bool 33 httpClient *http.Client 34 storage *storage.SQLITEManager 35 clients []*client 36 connect chan *session.Session 37 disconnect chan *session.Session 38 } 39 type client struct { 40 session *session.Session 41 class int 42 } 43 type config struct { 44 IntrospectTokenURL string `yaml:"IntrospectTokenURL"` 45 Port string `yaml:"Port"` 46 FileLocation string `yaml:"FileLocation"` 47 } 48 49 func (cfg *config) blockDir() string { 50 return cfg.FileLocation + "/block/" 51 } 52 func (cfg *config) fileDir() string { 53 return cfg.FileLocation + "/file/" 54 } 55 func (cfg *config) tempDir() string { 56 return cfg.FileLocation + "/tmp/" 57 } 58 59 func NewFileSyncServer(config_file_path string, skip_tls_verify bool) *FileSyncServer { 60 s := &FileSyncServer{} 61 //s.storage = &storage.SQLITEManager{} 62 //s.storage.Initialize() 63 s.clients = []*client{} 64 s.connect = make(chan *session.Session) 65 s.disconnect = make(chan *session.Session) 66 s.skip_tls_verify = skip_tls_verify 67 s.httpClient = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: skip_tls_verify}}} 68 http.DefaultClient = s.httpClient 69 s.config = new(config) 70 b, err := ioutil.ReadFile(config_file_path) 71 if err != nil { 72 log.Fatal(err) 73 } 74 err = yaml.Unmarshal(b, s.config) 75 if err != nil { 76 log.Fatal(err) 77 } 78 err = os.MkdirAll(s.config.blockDir(), os.ModePerm) 79 if err != nil { 80 log.Fatal(err) 81 } 82 err = os.MkdirAll(s.config.fileDir(), os.ModePerm) 83 if err != nil { 84 log.Fatal(err) 85 } 86 err = os.MkdirAll(s.config.tempDir(), os.ModePerm) 87 if err != nil { 88 log.Fatal(err) 89 } 90 return s 91 } 92 func (s *FileSyncServer) Serve() { 93 // Listen on TCP port 2000 on all available unicast and 94 // anycast IP addresses of the local system. 95 l, err := net.Listen("tcp", ":"+s.config.Port) 96 log.Println("accepting tcp connections on port", s.config.Port) 97 if err != nil { 98 log.Fatal(err) 99 } 100 defer l.Close() 101 // Handle the sessions in a new goroutine. 102 go s.serveSessions() 103 for { 104 // Wait for a connection. 105 conn, err := l.Accept() 106 if err != nil { 107 log.Fatal(err) 108 } 109 s.connect <- session.NewSession(conn) 110 } 111 } 112 func checkFile(file models.File, fullPath string) { 113 if file.IsHidden { 114 err := x.HideFile(fullPath) 115 if err != nil { 116 panic(err) 117 } 118 } 119 } 120 func (s *FileSyncServer) checkToken(msg *message.Message) (user_id string, token *oauth2.Token, err error) { 121 tokenstr := msg.Header[internal.TokenHeaderKey] 122 if tokenstr == nil { 123 return "", nil, errors.New("token is missing") 124 } 125 token = &oauth2.Token{AccessToken: tokenstr.(string)} 126 rar := common.NewRestApiRequest("GET", s.config.IntrospectTokenURL, nil).SetAuthHeader(token) 127 resp, err := internal.RestApiClient().Do(rar) 128 if err != nil { 129 return "", nil, err 130 } 131 m, err := common.ReadAsMap(resp.Body) 132 if err != nil { 133 return "", nil, err 134 } 135 if m["error"] != nil { 136 return "", nil, errors.New(m["error"].(string)) 137 } 138 data := m["data"].(map[string]interface{}) 139 isActive := data["active"].(bool) 140 if !isActive { 141 return "", nil, errors.New("the token is not valid") 142 } 143 sub := data["sub"].(string) 144 return sub, token, nil 145 } 146 func (s *FileSyncServer) serveSessions() { 147 for { 148 select { 149 case connect := <-s.connect: 150 client := &client{session: connect, class: 1} 151 s.clients = append(s.clients, client) 152 go s.serveClient(client) 153 case disconect := <-s.disconnect: 154 disconect.Close() 155 for index, item := range s.clients { 156 if item.session == disconect { 157 s.clients = append(s.clients[:index], s.clients[index+1:]...) 158 break 159 } 160 } 161 } 162 } 163 } 164 func (s *FileSyncServer) serveClient(client *client) { 165 session := client.session 166 defer func() { 167 if err := recover(); err != nil { 168 log.Println("disconnect session:", session, "cause:", err) 169 s.disconnect <- session 170 } 171 }() 172 log.Println("New session:", session) 173 for { 174 msg, err := session.ReadMessage() 175 if err != nil { 176 panic(err) 177 } 178 switch msg.MsgType { 179 case message.MT_PING: 180 reply_msg := new(message.Message) 181 reply_msg.MsgType = message.MT_PANG 182 session.SendMessage(reply_msg, nil, 0) 183 case message.MT_FILE: 184 _, token, err := s.checkToken(msg) 185 if err != nil { 186 panic(err) 187 } 188 file_path := s.config.fileDir() + msg.Header["path"].(string) 189 md5 := msg.Header["md5"].(string) 190 file_size := int64(msg.Header["file_size"].(float64)) 191 uploaded_size := int64(msg.Header["uploaded_size"].(float64)) 192 server_file_id := msg.Header["server_file_id"].(string) 193 block_name := uuid.New().String() 194 block_path := s.config.blockDir() + block_name 195 f, err := os.Create(block_path) 196 if err != nil { 197 panic(err) 198 } 199 written, err := io.CopyN(f, session, msg.BodySize) 200 if err != nil { 201 log.Println("receive file block failed:", err) 202 } else { 203 log.Println("received a new file block") 204 } 205 start := uploaded_size 206 end := written + start 207 //record uploaded file block 208 parameters := url.Values{} 209 parameters.Add("server_file_id", server_file_id) 210 parameters.Add("name", block_name) 211 parameters.Add("start", strconv.FormatInt(start, 10)) 212 parameters.Add("end", strconv.FormatInt(end, 10)) 213 rar := common.NewRestApiRequest("POST", internal.GetApiUrlPath("file-block"), []byte(parameters.Encode())).SetAuthHeader(token) 214 _, err = internal.RestApiClient().Do(rar) 215 if err != nil { 216 panic(err) 217 } 218 //assemble files if bytes of whole file has uploaded 219 if end == file_size { 220 //query all file blocks 221 rar := common.NewRestApiRequest("GET", internal.GetApiUrlPath("file-block")+"?server_file_id="+server_file_id, nil).SetAuthHeader(token) 222 resp, err := internal.RestApiClient().Do(rar) 223 if err != nil { 224 panic(err) 225 } 226 m, err := common.ReadAsMap(resp.Body) 227 if err != nil { 228 panic(err) 229 } 230 data := m["data"].([]interface{}) 231 //create temp file 232 temp_file_path := s.config.tempDir() + uuid.New().String() 233 temp_file, err := os.Create(temp_file_path) 234 if err != nil { 235 panic(err) 236 } 237 for i := len(data) - 1; i >= 0; i-- { 238 block := data[i].(map[string]interface{}) 239 log.Println(block) 240 block_path := block["Path"].(string) 241 block_file, err := os.Open(s.config.blockDir() + block_path) 242 if err != nil { 243 panic(err) 244 } 245 //copy block content to temp file 246 _, err = io.Copy(temp_file, block_file) 247 if err != nil { 248 panic(err) 249 } 250 block_file.Close() 251 } 252 //close file stream 253 temp_file.Close() 254 //check md5 255 tmp_file_md5, err := x.Hash_file_md5(temp_file_path) 256 if err != nil { 257 panic(err) 258 } 259 if tmp_file_md5 != md5 { 260 panic("the md5 is inconsistent") 261 } 262 //already assembled successfully, then change temp file name to final file name 263 err = os.Rename(temp_file_path, file_path) 264 if err != nil { 265 panic(err) 266 } 267 //change status 268 rar = common.NewRestApiRequest("PUT", internal.GetApiUrlPath("file"), []byte(fmt.Sprintf("server_file_id=%s", server_file_id))).SetAuthHeader(token) 269 _, err = internal.RestApiClient().Do(rar) 270 if err != nil { 271 panic(err) 272 } 273 } 274 reply := message.NewMessage(message.MT_Reply) 275 session.Send(reply, nil) 276 case message.MT_Request_Repeat: 277 case message.MT_Download_File: 278 file_path := s.config.fileDir() + msg.Header["path"].(string) 279 err := session.SendFile(file_path, func(filename string, md5 string, size int64) (int64, bool) { 280 return 0, true 281 }) 282 if err != nil { 283 panic(err) 284 } 285 case message.MT_DISCONNECT: 286 panic(errors.New("peer requested to disconnect connections")) 287 case message.MT_SYNC: 288 289 } 290 } 291 }