- commit
- 0dd16c8ba2b9370902c655e299e807de0f3eca72
- parent
- 87bca7e07eed9ad5e15f54d9ada65a4aa84a9e69
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2020-05-05 04:42
add file storage
Diffstat
| M | README.md | 26 | +++++++++++++++++--------- |
| M | via.go | 102 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------- |
| M | via.nginx | 4 | ++-- |
| M | via.service | 6 | +++--- |
4 files changed, 106 insertions, 32 deletions
diff --git a/README.md b/README.md
@@ -9,23 +9,31 @@ This is very much inspired by <https://patchbay.pub/> and its clones 9 9 10 10 Start the server: 11 1112 -1 via [-v] [port]-1 12 via [-v] [-d storage_dir] [port] 13 13 14 14 Then start sending requests on the client: 15 15 16 16 # start listening for server sent event stream17 -1 curl http://localhost:8001/someid-1 17 curl http://localhost:8001/msg/someid 18 1819 -1 # POST some data20 -1 curl http://localhost:8001/someid -d somedata-1 19 # POST a message -1 20 curl http://localhost:8001/msg/someid -d somedata 21 2122 -1 You can also protect your ID with a password so no one else can listen to23 -1 it at the same time:-1 22 # Store, get, and delete a document -1 23 curl http://localhost:8001/store/someid -d someid -1 24 curl http://localhost:8001/store/someid -1 25 curl http://localhost:8001/store/someid -X DELETE 24 2625 -1 curl http://localhost:8001/someid:somepassword26 -1 curl http://localhost:8001/someid # 40327 -1 curl http://localhost:8001/someid -d somedata # 200-1 27 You can also protect your message ID with a password so no one else can listen -1 28 to it at the same time: 28 29 -1 30 curl http://localhost:8001/msg/someid:somepassword -1 31 curl http://localhost:8001/msg/someid # 403 -1 32 curl http://localhost:8001/msg/someid -d somedata # 200 -1 33 -1 34 You should regularly clean up old files: -1 35 -1 36 find {storage_dir} -type f -mtime +7 -delete 29 37 30 38 ## Differences to patchbay 31 39
diff --git a/via.go b/via.go
@@ -1,21 +1,14 @@1 -1 // Simple pubsub server inspired by https://patchbay.pub2 -1 //3 -1 // Usage: via [-v] [port]4 -1 // curl http://localhost:8001/someid # server sent event stream5 -1 // curl http://localhost:8001/someid -d somedata6 -1 //7 -1 // curl http://localhost:8001/someid:somepassword8 -1 // curl http://localhost:8001/someid # 4039 -1 // curl http://localhost:8001/someid -d somedata # 20010 1 package main 11 2 12 3 import ( -1 4 "encoding/base64" 13 5 "flag" 14 6 "fmt" 15 7 "io/ioutil" 16 8 "log" 17 9 "net/http" 18 10 "os" -1 11 "path" 19 12 "strings" 20 13 "sync" 21 14 "time" @@ -30,6 +23,7 @@ type Topic struct { 30 23 var mux = &sync.RWMutex{} 31 24 var topics = make(map[string]Topic) 32 25 var verbose = false -1 26 var dir = "" 33 27 34 28 func splitPassword(combined string) (string, string) { 35 29 split := strings.SplitN(combined, ":", 2) @@ -83,7 +77,12 @@ func popChannel(key string, ch chan []byte) { 83 77 } 84 78 } 85 7986 -1 func post(w http.ResponseWriter, r *http.Request) {-1 80 func getPath(key string) string { -1 81 hash := base64.URLEncoding.EncodeToString([]byte(key)) -1 82 return path.Join(dir, hash) -1 83 } -1 84 -1 85 func postMsg(w http.ResponseWriter, r *http.Request) { 87 86 key, password := splitPassword(r.URL.Path) 88 87 89 88 if password != "" { @@ -116,7 +115,7 @@ func post(w http.ResponseWriter, r *http.Request) { 116 115 } 117 116 } 118 117119 -1 func get(w http.ResponseWriter, r *http.Request) {-1 118 func getMsg(w http.ResponseWriter, r *http.Request) { 120 119 key, password := splitPassword(r.URL.Path) 121 120 122 121 ch := make(chan []byte) @@ -158,15 +157,79 @@ func get(w http.ResponseWriter, r *http.Request) { 158 157 } 159 158 } 160 159161 -1 func handler(w http.ResponseWriter, r *http.Request) {-1 160 func putStore(w http.ResponseWriter, r *http.Request) { -1 161 path := getPath(r.URL.Path) -1 162 -1 163 content, err := ioutil.ReadAll(r.Body) -1 164 if err != nil { -1 165 log.Println("error reading request body:", err) -1 166 http.Error(w, "Internal Server Error", http.StatusInternalServerError) -1 167 return -1 168 } -1 169 -1 170 err = ioutil.WriteFile(path, content, 0644) -1 171 if err != nil { -1 172 log.Println("error writing to file:", path, err) -1 173 http.Error(w, "Internal Server Error", http.StatusInternalServerError) -1 174 return -1 175 } -1 176 } -1 177 -1 178 func getStore(w http.ResponseWriter, r *http.Request) { -1 179 path := getPath(r.URL.Path) -1 180 -1 181 content, err := ioutil.ReadFile(path) -1 182 if os.IsNotExist(err) { -1 183 http.Error(w, "Not Found", http.StatusNotFound) -1 184 return -1 185 } else if err != nil { -1 186 log.Println("error reading from file:", path, err) -1 187 http.Error(w, "Internal Server Error", http.StatusInternalServerError) -1 188 return -1 189 } -1 190 -1 191 w.Write(content) -1 192 } -1 193 -1 194 func deleteStore(w http.ResponseWriter, r *http.Request) { -1 195 path := getPath(r.URL.Path) -1 196 -1 197 err := os.Remove(path) -1 198 if os.IsNotExist(err) { -1 199 http.Error(w, "Not Found", http.StatusNotFound) -1 200 return -1 201 } else if err != nil { -1 202 log.Println("error removing file:", path, err) -1 203 http.Error(w, "Internal Server Error", http.StatusInternalServerError) -1 204 return -1 205 } -1 206 } -1 207 -1 208 func handleMsg(w http.ResponseWriter, r *http.Request) { 162 209 if verbose { 163 210 log.Println(r.Method, r.URL) 164 211 } 165 212166 -1 if r.Method == "GET" {167 -1 get(w, r)168 -1 } else if r.Method == "POST" {169 -1 post(w, r)-1 213 if r.Method == http.MethodGet { -1 214 getMsg(w, r) -1 215 } else if r.Method == http.MethodPost { -1 216 postMsg(w, r) -1 217 } else { -1 218 http.Error(w, "Unsupported Method", http.StatusMethodNotAllowed) -1 219 } -1 220 } -1 221 -1 222 func handleStore(w http.ResponseWriter, r *http.Request) { -1 223 if verbose { -1 224 log.Println(r.Method, r.URL) -1 225 } -1 226 -1 227 if r.Method == http.MethodPut || r.Method == http.MethodPost { -1 228 putStore(w, r) -1 229 } else if r.Method == http.MethodGet || r.Method == http.MethodHead { -1 230 getStore(w, r) -1 231 } else if r.Method == http.MethodDelete { -1 232 deleteStore(w, r) 170 233 } else { 171 234 http.Error(w, "Unsupported Method", http.StatusMethodNotAllowed) 172 235 } @@ -179,14 +242,17 @@ func main() { 179 242 } 180 243 181 244 flag.BoolVar(&verbose, "v", false, "enable verbose logs") -1 245 flag.StringVar(&dir, "d", ".", "directory for storage") 182 246 flag.Parse() 183 247 184 248 addr := "localhost:8001" 185 249 if len(flag.Args()) > 0 {186 -1 addr = fmt.Sprintf("localhost:%s", flag.Args()[0]-1 250 addr = fmt.Sprintf("localhost:%s", flag.Args()[0]) 187 251 } 188 252189 -1 http.HandleFunc("/", handler)-1 253 http.HandleFunc("/msg/", handleMsg) -1 254 http.HandleFunc("/store/", handleStore) -1 255 190 256 log.Printf("Serving on http://%s", addr) 191 257 log.Fatal(http.ListenAndServe(addr, nil)) 192 258 }
diff --git a/via.nginx b/via.nginx
@@ -8,7 +8,7 @@ server {
8 8 # https://enable-cors.org/server_nginx.html
9 9 if ($request_method = 'OPTIONS') {
10 10 add_header 'Access-Control-Allow-Origin' '*';
11 -1 add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
-1 11 add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, HEAD, OPTIONS';
12 12 add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
13 13 add_header 'Access-Control-Max-Age' 1728000;
14 14 add_header 'Content-Type' 'text/plain; charset=utf-8';
@@ -16,7 +16,7 @@ server {
16 16 return 204;
17 17 }
18 18 add_header 'Access-Control-Allow-Origin' '*';
19 -1 add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
-1 19 add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, HEAD, OPTIONS';
20 20 add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
21 21
22 22 add_header 'Link' '<https://github.com/xi/via>; rel="help"';
diff --git a/via.service b/via.service
@@ -4,10 +4,10 @@ After=network.target 4 4 5 5 [Service] 6 6 Type=simple7 -1 ExecStart=/usr/bin/via 8001-1 7 ExecStart=/usr/bin/via -d /var/via 8001 8 8 Restart=always9 -1 DynamicUser=yes10 -1 ProtectSystem=strict-1 9 User=via -1 10 ProtectSystem=full 11 11 ProtectHome=yes 12 12 MemoryDenyWriteExecute=yes 13 13