This commit is contained in:
parent
3fe9d65cdf
commit
70f5fb82f9
18
.pre-commit-config.yaml
Normal file
18
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.1.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-merge-conflict
|
||||
- id: check-added-large-files
|
||||
- repo: https://github.com/dnephin/pre-commit-golang
|
||||
rev: master
|
||||
hooks:
|
||||
- id: go-vet
|
||||
- id: go-fmt
|
||||
- id: go-lint
|
||||
- id: go-unit-tests
|
|
@ -1,19 +1,5 @@
|
|||
/*
|
||||
Microsub server
|
||||
Copyright (C) 2018 Peter Stuifzand
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Package main contains the main command for the Microsub server.
|
||||
*/
|
||||
package main
|
||||
|
||||
|
@ -77,7 +63,7 @@ func WithAuth(handler http.Handler, b *memoryBackend) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
if token.Me != b.Me {
|
||||
if token.Me != b.Me { // FIXME: Me should be part of the request
|
||||
log.Printf("Missing \"me\" in token response: %#v\n", token)
|
||||
http.Error(w, "Wrong me", 403)
|
||||
return
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Microsub server
|
||||
Package main runs the microsub server
|
||||
|
||||
Copyright (C) 2018 Peter Stuifzand
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -42,6 +43,7 @@ import (
|
|||
"willnorris.com/go/microformats"
|
||||
)
|
||||
|
||||
// DefaultPrio is the priority value for new channels
|
||||
const DefaultPrio = 9999999
|
||||
|
||||
type memoryBackend struct {
|
||||
|
@ -51,10 +53,10 @@ type memoryBackend struct {
|
|||
Channels map[string]microsub.Channel
|
||||
Feeds map[string][]microsub.Feed
|
||||
Settings map[string]channelSetting
|
||||
NextUid int
|
||||
NextUID int
|
||||
|
||||
Me string
|
||||
TokenEndpoint string
|
||||
Me string // FIXME: should be removed
|
||||
TokenEndpoint string // FIXME: should be removed
|
||||
AuthEnabled bool
|
||||
|
||||
ticker *time.Ticker
|
||||
|
@ -68,6 +70,7 @@ type channelSetting struct {
|
|||
IncludeRegex string
|
||||
}
|
||||
|
||||
// Debug interface for easy of use in other packages
|
||||
type Debug interface {
|
||||
Debug()
|
||||
}
|
||||
|
@ -176,7 +179,8 @@ func createMemoryBackend() {
|
|||
backend.Channels[c.UID] = c
|
||||
}
|
||||
|
||||
backend.NextUid = 1000000
|
||||
backend.NextUID = 1000000
|
||||
// FIXME: can't be used in Backend
|
||||
backend.Me = "https://example.com/"
|
||||
|
||||
backend.lock.Unlock()
|
||||
|
@ -443,24 +447,24 @@ func (b *memoryBackend) Search(query string) ([]microsub.Feed, error) {
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
fetchUrl, err := url.Parse(u)
|
||||
md := microformats.Parse(resp.Body, fetchUrl)
|
||||
fetchURL, err := url.Parse(u)
|
||||
md := microformats.Parse(resp.Body, fetchURL)
|
||||
if err != nil {
|
||||
log.Printf("Error while fetching %s: %v\n", u, err)
|
||||
continue
|
||||
}
|
||||
|
||||
feedResp, err := Fetch2(fetchUrl.String())
|
||||
feedResp, err := Fetch2(fetchURL.String())
|
||||
if err != nil {
|
||||
log.Printf("Error in fetch of %s - %v\n", fetchUrl, err)
|
||||
log.Printf("Error in fetch of %s - %v\n", fetchURL, err)
|
||||
continue
|
||||
}
|
||||
defer feedResp.Body.Close()
|
||||
|
||||
// TODO: Combine FeedHeader and FeedItems so we can use it here
|
||||
parsedFeed, err := fetch.FeedHeader(&fetch2{}, fetchUrl.String(), feedResp.Header.Get("Content-Type"), feedResp.Body)
|
||||
parsedFeed, err := fetch.FeedHeader(&fetch2{}, fetchURL.String(), feedResp.Header.Get("Content-Type"), feedResp.Body)
|
||||
if err != nil {
|
||||
log.Printf("Error in parse of %s - %v\n", fetchUrl, err)
|
||||
log.Printf("Error in parse of %s - %v\n", fetchURL, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -706,7 +710,7 @@ func Fetch2(fetchURL string) (*http.Response, error) {
|
|||
client := http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetch failed: %s", u, err)
|
||||
return nil, fmt.Errorf("fetch failed: %s: %s", u, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
@ -724,10 +728,11 @@ func Fetch2(fetchURL string) (*http.Response, error) {
|
|||
}
|
||||
|
||||
func (b *memoryBackend) createChannel(name string) microsub.Channel {
|
||||
uid := fmt.Sprintf("%012d", b.NextUid)
|
||||
uid := fmt.Sprintf("%012d", b.NextUID)
|
||||
channel := microsub.Channel{
|
||||
UID: uid,
|
||||
Name: name,
|
||||
Unread: microsub.Unread{microsub.UnreadCount, false, 0},
|
||||
}
|
||||
return channel
|
||||
}
|
||||
|
@ -737,7 +742,7 @@ func (b *memoryBackend) setChannel(channel microsub.Channel) {
|
|||
defer b.lock.Unlock()
|
||||
b.Channels[channel.UID] = channel
|
||||
b.Feeds[channel.UID] = []microsub.Feed{}
|
||||
b.NextUid++
|
||||
b.NextUID++
|
||||
}
|
||||
|
||||
func updateChannelInRedis(conn redis.Conn, uid string, prio int) {
|
||||
|
|
|
@ -292,7 +292,9 @@ func (timeline *redisStreamTimeline) Items(before, after string) (microsub.Timel
|
|||
|
||||
return microsub.Timeline{
|
||||
Items: items,
|
||||
Paging: microsub.Pagination{},
|
||||
Paging: microsub.Pagination{
|
||||
After: items[len(items)-1].ID,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"p83.nl/go/ekster/pkg/microsub"
|
||||
)
|
||||
|
||||
// Client is a HTTP client for Microsub
|
||||
type Client struct {
|
||||
Me *url.URL
|
||||
MicrosubEndpoint *url.URL
|
||||
|
@ -120,6 +121,7 @@ func (c *Client) microsubPostFormRequest(action string, args map[string]string,
|
|||
return res, err
|
||||
}
|
||||
|
||||
// ChannelsGetList gets the channels from a Microsub server
|
||||
func (c *Client) ChannelsGetList() ([]microsub.Channel, error) {
|
||||
args := make(map[string]string)
|
||||
res, err := c.microsubGetRequest("channels", args)
|
||||
|
@ -146,6 +148,7 @@ func (c *Client) ChannelsGetList() ([]microsub.Channel, error) {
|
|||
return channels.Channels, err
|
||||
}
|
||||
|
||||
// TimelineGet gets a timeline from a Microsub server
|
||||
func (c *Client) TimelineGet(before, after, channel string) (microsub.Timeline, error) {
|
||||
args := make(map[string]string)
|
||||
args["after"] = after
|
||||
|
@ -172,6 +175,7 @@ func (c *Client) TimelineGet(before, after, channel string) (microsub.Timeline,
|
|||
return timeline, nil
|
||||
}
|
||||
|
||||
// PreviewURL gets a Timeline for a url from a Microsub server
|
||||
func (c *Client) PreviewURL(url string) (microsub.Timeline, error) {
|
||||
args := make(map[string]string)
|
||||
args["url"] = url
|
||||
|
@ -338,7 +342,3 @@ func (c *Client) MarkRead(channel string, uids []string) error {
|
|||
res.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) AddEventListener(el microsub.EventListener) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
/*
|
||||
Microsub server
|
||||
Copyright (C) 2018 Peter Stuifzand
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Package microsub describes the protocol methods of the Microsub protocol
|
||||
package microsub
|
||||
|
||||
|
@ -34,11 +16,13 @@ import (
|
|||
block / unblock
|
||||
*/
|
||||
|
||||
// Constants for Unread
|
||||
const (
|
||||
UnreadBool = 0
|
||||
UnreadCount = 1
|
||||
)
|
||||
|
||||
// Unread is a special int/bool value for the JSON response
|
||||
type Unread struct {
|
||||
Type int
|
||||
Unread bool
|
||||
|
|
84
pkg/microsub/protocol_test.go
Normal file
84
pkg/microsub/protocol_test.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package microsub
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_UnmarshalUnreadBool(t *testing.T) {
|
||||
var x Unread
|
||||
err := json.Unmarshal([]byte("false"), &x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, UnreadBool, x.Type)
|
||||
assert.False(t, x.Unread)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_UnmarshalUnreadBoolTrue(t *testing.T) {
|
||||
var x Unread
|
||||
err := json.Unmarshal([]byte("true"), &x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, UnreadBool, x.Type)
|
||||
assert.True(t, x.Unread)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_UnmarshalUnreadIntZero(t *testing.T) {
|
||||
var x Unread
|
||||
err := json.Unmarshal([]byte("0"), &x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, UnreadCount, x.Type)
|
||||
assert.Equal(t, 0, x.UnreadCount)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_UnmarshalUnreadIntNonZero(t *testing.T) {
|
||||
var x Unread
|
||||
err := json.Unmarshal([]byte("209449"), &x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, UnreadCount, x.Type)
|
||||
assert.Equal(t, 209449, x.UnreadCount)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MarshalUnreadEmpty(t *testing.T) {
|
||||
x := Unread{}
|
||||
bytes, err := json.Marshal(x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, "false", string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MarshalUnreadBoolFalse(t *testing.T) {
|
||||
x := Unread{Type: UnreadBool, Unread: false}
|
||||
bytes, err := json.Marshal(x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, "false", string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MarshalUnreadBoolTrue(t *testing.T) {
|
||||
x := Unread{Type: UnreadBool, Unread: true}
|
||||
bytes, err := json.Marshal(x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, "true", string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MarshalUnreadIntZero(t *testing.T) {
|
||||
x := Unread{Type: UnreadCount, UnreadCount: 0}
|
||||
bytes, err := json.Marshal(x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, "0", string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MarshalUnreadIntNonZero(t *testing.T) {
|
||||
x := Unread{Type: UnreadCount, UnreadCount: 1884844}
|
||||
bytes, err := json.Marshal(x)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, "1884844", string(bytes))
|
||||
}
|
||||
}
|
|
@ -1,19 +1,6 @@
|
|||
/*
|
||||
ekster - microsub server
|
||||
Copyright (C) 2018 Peter Stuifzand
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Package server contains the microsub server itself. It implements http.Handler.
|
||||
It follows the spec at https://indieweb.org/Microsub-spec.
|
||||
*/
|
||||
package server
|
||||
|
||||
|
@ -31,6 +18,7 @@ var (
|
|||
entryRegex = regexp.MustCompile("^entry\\[\\d+\\]$")
|
||||
)
|
||||
|
||||
// Constants used for the responses
|
||||
const (
|
||||
OutputContentType = "application/json; charset=utf-8"
|
||||
)
|
||||
|
@ -51,11 +39,14 @@ func respondJSON(w http.ResponseWriter, value interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// NewMicrosubHandler is the main entry point for the microsub server
|
||||
// It returns a handler for HTTP and a broker that will send events.
|
||||
func NewMicrosubHandler(backend microsub.Microsub) (http.Handler, *Broker) {
|
||||
broker := NewServer()
|
||||
return µsubHandler{backend, broker}, broker
|
||||
}
|
||||
|
||||
// Methods required by http.Handler
|
||||
func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
func createServerClient() (*httptest.Server, *client.Client) {
|
||||
backend := &NullBackend{}
|
||||
|
||||
handler := NewMicrosubHandler(backend)
|
||||
handler, _ := NewMicrosubHandler(backend)
|
||||
|
||||
server := httptest.NewServer(handler)
|
||||
|
||||
|
|
|
@ -25,10 +25,6 @@ import (
|
|||
type NullBackend struct {
|
||||
}
|
||||
|
||||
func (b *NullBackend) AddEventListener(el microsub.EventListener) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// ChannelsGetList gets no channels
|
||||
func (b *NullBackend) ChannelsGetList() ([]microsub.Channel, error) {
|
||||
return []microsub.Channel{
|
||||
|
@ -66,26 +62,31 @@ func (b *NullBackend) TimelineGet(before, after, channel string) (microsub.Timel
|
|||
}, nil
|
||||
}
|
||||
|
||||
// FollowGetList implements the follow list command
|
||||
func (b *NullBackend) FollowGetList(uid string) ([]microsub.Feed, error) {
|
||||
return []microsub.Feed{
|
||||
{Name: "test", Type: "feed", URL: "https://example.com/"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FollowURL follows a new url
|
||||
func (b *NullBackend) FollowURL(uid string, url string) (microsub.Feed, error) {
|
||||
return microsub.Feed{Type: "feed", URL: url}, nil
|
||||
}
|
||||
|
||||
// UnfollowURL unfollows a url
|
||||
func (b *NullBackend) UnfollowURL(uid string, url string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search search for a query and return an example list of feeds
|
||||
func (b *NullBackend) Search(query string) ([]microsub.Feed, error) {
|
||||
return []microsub.Feed{
|
||||
{"feed", "https://example.com/", "Example", "test.jpg", "test", microsub.Card{}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PreviewURL shows an empty feed
|
||||
func (b *NullBackend) PreviewURL(url string) (microsub.Timeline, error) {
|
||||
return microsub.Timeline{
|
||||
Paging: microsub.Pagination{},
|
||||
|
@ -93,6 +94,7 @@ func (b *NullBackend) PreviewURL(url string) (microsub.Timeline, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// MarkRead marks no items as read
|
||||
func (b *NullBackend) MarkRead(channel string, uids []string) error {
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user