via

Simple pubsub server inspired by https://patchbay.pub/
git clone https://git.ce9e.org/via.git

commit
b5f02bd0e6629c15873e19abddbe68cdfb90bf82
parent
662645fa864b50c949101be777a60b0e39182802
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2025-06-07 15:55
rm passwords

fixes #2

Diffstat

M README.md 7 -------
M via.go 77 ++++++++++++++-----------------------------------------------

2 files changed, 17 insertions, 67 deletions


diff --git a/README.md b/README.md

@@ -34,13 +34,6 @@ Use the `hmsg` prefix if you want to keep a history:
   34    34 	# the `historyRemaining` field in POST responses tells you how much space is left
   35    35 	curl http://localhost:8001/hmsg/someid -d combined -H 'Last-Event-Id: 3' -X PUT
   36    36 
   37    -1 You can also protect your ID with a password so no one else can listen to
   38    -1 it at the same time:
   39    -1 
   40    -1 	curl http://localhost:8001/msg/someid:somepassword
   41    -1 	curl http://localhost:8001/msg/someid  # 403
   42    -1 	curl http://localhost:8001/msg/someid -d somedata  # 200
   43    -1 
   44    37 You should regularly clean up old files:
   45    38 
   46    39 	find {storage_dir} -type f -mtime +7 -delete

diff --git a/via.go b/via.go

@@ -28,7 +28,6 @@ type Msg struct {
   28    28 type Topic struct {
   29    29 	sync.Mutex
   30    30 	channels   map[chan Msg]bool
   31    -1 	password   string
   32    31 	hasHistory bool
   33    32 	history    []Msg
   34    33 	lastId     int
@@ -40,29 +39,19 @@ var verbose = false
   40    39 var maxHistorySize = 100
   41    40 var dir = ""
   42    41 
   43    -1 func splitPassword(combined string) (string, string) {
   44    -1 	split := strings.SplitN(combined, ":", 2)
   45    -1 	if len(split) == 2 {
   46    -1 		return split[0], split[1]
   47    -1 	} else {
   48    -1 		return combined, ""
   49    -1 	}
   50    -1 }
   51    -1 
   52    42 func getStorePath(key string) string {
   53    43 	hash := base64.URLEncoding.EncodeToString([]byte(key))
   54    44 	return path.Join(dir, hash)
   55    45 }
   56    46 
   57    47 func (topic *Topic) storeHistory(key string) {
   58    -1 	path := getStorePath(fmt.Sprintf("%s:%s", key, topic.password))
   59    -1 
   60    48 	content, err := json.Marshal(topic.history)
   61    49 	if err != nil {
   62    50 		log.Println("error storing history:", err)
   63    51 		return
   64    52 	}
   65    53 
   -1    54 	path := getStorePath(key)
   66    55 	err = ioutil.WriteFile(path, content, 0644)
   67    56 	if err != nil {
   68    57 		log.Println("error storing history:", err)
@@ -71,7 +60,7 @@ func (topic *Topic) storeHistory(key string) {
   71    60 }
   72    61 
   73    62 func (topic *Topic) restoreHistory(key string) {
   74    -1 	path := getStorePath(fmt.Sprintf("%s:%s", key, topic.password))
   -1    63 	path := getStorePath(key)
   75    64 
   76    65 	content, err := ioutil.ReadFile(path)
   77    66 	if err != nil {
@@ -95,8 +84,7 @@ func (topic *Topic) restoreHistory(key string) {
   95    84 }
   96    85 
   97    86 func (topic *Topic) deleteHistory(key string) {
   98    -1 	path := getStorePath(fmt.Sprintf("%s:%s", key, topic.password))
   99    -1 
   -1    87 	path := getStorePath(key)
  100    88 	err := os.Remove(path)
  101    89 	if err != nil && !os.IsNotExist(err) {
  102    90 		log.Println("error deleting history:", err)
@@ -139,7 +127,7 @@ func (topic *Topic) put(data []byte, lastId int) {
  139   127 	}
  140   128 }
  141   129 
  142    -1 func getTopic(key string, password string) (*Topic, bool) {
   -1   130 func getTopic(key string) *Topic {
  143   131 	mux.RLock()
  144   132 	topic, ok := topics[key]
  145   133 	mux.RUnlock()
@@ -147,7 +135,6 @@ func getTopic(key string, password string) (*Topic, bool) {
  147   135 	if !ok {
  148   136 		topic = &Topic{
  149   137 			channels:   make(map[chan Msg]bool, 0),
  150    -1 			password:   password,
  151   138 			hasHistory: strings.HasPrefix(key, "/hmsg/"),
  152   139 			history:    make([]Msg, 0),
  153   140 			lastId:     0,
@@ -158,18 +145,13 @@ func getTopic(key string, password string) (*Topic, bool) {
  158   145 		mux.Lock()
  159   146 		topics[key] = topic
  160   147 		mux.Unlock()
  161    -1 	} else if topic.password != password {
  162    -1 		return nil, false
  163   148 	}
  164   149 
  165    -1 	return topic, true
   -1   150 	return topic
  166   151 }
  167   152 
  168    -1 func pushChannel(key string, password string, ch chan Msg, lastId int) bool {
  169    -1 	topic, allowed := getTopic(key, password)
  170    -1 	if !allowed {
  171    -1 		return false
  172    -1 	}
   -1   153 func pushChannel(key string, ch chan Msg, lastId int) {
   -1   154 	topic := getTopic(key)
  173   155 
  174   156 	go func() {
  175   157 		topic.Lock()
@@ -183,8 +165,6 @@ func pushChannel(key string, password string, ch chan Msg, lastId int) bool {
  183   165 
  184   166 		topic.channels[ch] = true
  185   167 	}()
  186    -1 
  187    -1 	return true
  188   168 }
  189   169 
  190   170 func popChannel(key string, ch chan Msg) {
@@ -207,13 +187,6 @@ func popChannel(key string, ch chan Msg) {
  207   187 }
  208   188 
  209   189 func post(w http.ResponseWriter, r *http.Request) {
  210    -1 	key, password := splitPassword(r.URL.Path)
  211    -1 
  212    -1 	if password != "" {
  213    -1 		http.Error(w, "Forbidden", http.StatusForbidden)
  214    -1 		return
  215    -1 	}
  216    -1 
  217   190 	body, err := ioutil.ReadAll(r.Body)
  218   191 	if err != nil {
  219   192 		log.Println("error reading request body:", err)
@@ -222,7 +195,7 @@ func post(w http.ResponseWriter, r *http.Request) {
  222   195 	}
  223   196 
  224   197 	mux.RLock()
  225    -1 	topic, ok := topics[key]
   -1   198 	topic, ok := topics[r.URL.Path]
  226   199 	mux.RUnlock()
  227   200 
  228   201 	response := make(map[string]int)
@@ -241,26 +214,20 @@ func post(w http.ResponseWriter, r *http.Request) {
  241   214 	topic.post(body)
  242   215 
  243   216 	if topic.hasHistory {
  244    -1 		topic.storeHistory(key)
   -1   217 		topic.storeHistory(r.URL.Path)
  245   218 		response["historyRemaining"] = maxHistorySize - len(topic.history)
  246   219 	}
  247   220 }
  248   221 
  249   222 func get(w http.ResponseWriter, r *http.Request) {
  250    -1 	key, password := splitPassword(r.URL.Path)
  251    -1 
  252   223 	lastId, err := strconv.Atoi(r.Header.Get("Last-Event-ID"))
  253   224 	if err != nil {
  254   225 		lastId = 0
  255   226 	}
  256   227 
  257   228 	ch := make(chan Msg)
  258    -1 	allowed := pushChannel(key, password, ch, lastId)
  259    -1 	if !allowed {
  260    -1 		http.Error(w, "Forbidden", http.StatusForbidden)
  261    -1 		return
  262    -1 	}
  263    -1 	defer popChannel(key, ch)
   -1   229 	pushChannel(r.URL.Path, ch, lastId)
   -1   230 	defer popChannel(r.URL.Path, ch)
  264   231 
  265   232 	ctx := r.Context()
  266   233 
@@ -294,14 +261,9 @@ func get(w http.ResponseWriter, r *http.Request) {
  294   261 }
  295   262 
  296   263 func put(w http.ResponseWriter, r *http.Request) {
  297    -1 	key, password := splitPassword(r.URL.Path)
  298    -1 
  299    -1 	topic, allowed := getTopic(key, password)
   -1   264 	topic := getTopic(r.URL.Path)
  300   265 
  301    -1 	if !allowed {
  302    -1 		http.Error(w, "Forbidden", http.StatusForbidden)
  303    -1 		return
  304    -1 	} else if !topic.hasHistory {
   -1   266 	if !topic.hasHistory {
  305   267 		http.Error(w, "No history", http.StatusBadRequest)
  306   268 		return
  307   269 	}
@@ -323,18 +285,13 @@ func put(w http.ResponseWriter, r *http.Request) {
  323   285 	defer topic.Unlock()
  324   286 
  325   287 	topic.put(body, lastId)
  326    -1 	topic.storeHistory(key)
   -1   288 	topic.storeHistory(r.URL.Path)
  327   289 }
  328   290 
  329   291 func del(w http.ResponseWriter, r *http.Request) {
  330    -1 	key, password := splitPassword(r.URL.Path)
   -1   292 	topic := getTopic(r.URL.Path)
  331   293 
  332    -1 	topic, allowed := getTopic(key, password)
  333    -1 
  334    -1 	if !allowed {
  335    -1 		http.Error(w, "Forbidden", http.StatusForbidden)
  336    -1 		return
  337    -1 	} else if !topic.hasHistory {
   -1   294 	if !topic.hasHistory {
  338   295 		http.Error(w, "No history", http.StatusBadRequest)
  339   296 		return
  340   297 	}
@@ -343,7 +300,7 @@ func del(w http.ResponseWriter, r *http.Request) {
  343   300 	defer topic.Unlock()
  344   301 
  345   302 	topic.history = make([]Msg, 0)
  346    -1 	topic.deleteHistory(key)
   -1   303 	topic.deleteHistory(r.URL.Path)
  347   304 }
  348   305 
  349   306 func handler(w http.ResponseWriter, r *http.Request) {