more thoughts

This commit is contained in:
David 2022-11-12 12:06:12 +07:00
parent dce1006228
commit 836a54599b
6 changed files with 133 additions and 35 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
config.json
/friend

BIN
friend

Binary file not shown.

View File

@ -7,38 +7,99 @@ import (
"github.com/go-chi/chi/v5"
)
type APIError struct {
Error string `json:"error"`
}
// since we only have the single username to worry about, we just wrap everything up here
func UserExistsMiddleware(user string, h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "user")
if userID != user {
RespondJSON(w, APIError{Error: "account not found"}, http.StatusNotFound)
return
}
h.ServeHTTP(w, r)
}
}
func RespondJSON(w http.ResponseWriter, content any, statuscode int) {
b, err := json.Marshal(content)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(statuscode)
w.Write(b)
}
func WebFingerHandler(wf *Webfinger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
requestedResource := r.URL.Query().Get("resource")
res, err := wf.FingerResource(requestedResource)
if err != nil {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(`{"error": "account not found"}`))
RespondJSON(w, APIError{Error: "account not found"}, http.StatusNotFound)
return
}
b, err := json.Marshal(res)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Write(b)
RespondJSON(w, res, http.StatusOK)
}
}
func ProfileHandler(pp *ProfileProvider) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "user")
res, err := pp.Get(userID)
res, err := pp.Get()
if err != nil {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(`{"error": "account not found"}`))
RespondJSON(w, APIError{Error: "account not found"}, http.StatusNotFound)
return
}
b, err := json.Marshal(res)
RespondJSON(w, res, http.StatusOK)
}
}
// client retrieves messages for user
func GetInboxHandler(streams *InMemoryStreams) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
stream, err := streams.GetActivitiesToUser()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
RespondJSON(w, APIError{Error: err.Error()}, http.StatusInternalServerError)
return
}
w.Write(b)
RespondJSON(w, stream, http.StatusOK)
}
}
// handle incoming activities for user
func PostInboxHandler(streams *InMemoryStreams) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := streams.AddActivityToUser()
if err != nil {
RespondJSON(w, APIError{Error: err.Error()}, http.StatusInternalServerError)
return
}
RespondJSON(w, nil, http.StatusOK)
}
}
// other servers/clients read a user's activities
func GetOutboxHandler(streams *InMemoryStreams) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
stream, err := streams.GetActivitiesFromUser()
if err != nil {
RespondJSON(w, APIError{Error: err.Error()}, http.StatusInternalServerError)
return
}
RespondJSON(w, stream, http.StatusOK)
}
}
// client adds new activity for user
func PostOutboxHandler(streams *InMemoryStreams) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := streams.AddActivityFromUser()
if err != nil {
RespondJSON(w, APIError{Error: err.Error()}, http.StatusInternalServerError)
return
}
RespondJSON(w, nil, http.StatusOK)
}
}

16
main.go
View File

@ -8,18 +8,28 @@ import (
)
func main() {
wf, err := NewWebfinger("dma", "hello.yetaga.in")
// this will be configurable in the future obviously
username := "dma"
domain := "hello.yetaga.in"
wf, err := NewWebfinger(username, domain)
if err != nil {
log.Fatalln(err)
}
pp, err := NewProfileProvider("hello.yetaga.in")
pp, err := NewProfileProvider(username, domain)
if err != nil {
log.Fatalln(err)
}
st := &InMemoryStreams{}
r := chi.NewRouter()
r.Get("/.well-known/webfinger", WebFingerHandler(wf))
r.Get("/users/{user}", ProfileHandler(pp))
r.Get("/users/{user}", UserExistsMiddleware(username, ProfileHandler(pp)))
r.Get("/users/{user}/inbox", UserExistsMiddleware(username, GetInboxHandler(st)))
r.Post("/users/{user}/inbox", UserExistsMiddleware(username, PostInboxHandler(st)))
r.Get("/users/{user}/outbox", UserExistsMiddleware(username, GetOutboxHandler(st)))
r.Post("/users/{user}/outbox", UserExistsMiddleware(username, PostOutboxHandler(st)))
r.Post("/inbox", PostInboxHandler(st)) // "shared" inbox
log.Println("listening on http://0.0.0.0:8008")
err = http.ListenAndServe(":8008", r)

View File

@ -13,7 +13,7 @@ type Actor struct {
PublicKey PublicKey `json:"publicKey,omitempty"`
Inbox string `json:"inbox,omitempty"`
Outbox string `json:"outbo,omitempty"`
Outbox string `json:"outbox,omitempty"`
Followers string `json:"followers,omitempty"`
Following string `json:"following,omitempty"`
@ -36,22 +36,24 @@ type PublicKey struct {
}
type ProfileProvider struct {
username string
domain string
}
func NewProfileProvider(domain string) (*ProfileProvider, error) {
func NewProfileProvider(username, domain string) (*ProfileProvider, error) {
return &ProfileProvider{
username: username,
domain: domain,
}, nil
}
func (pp *ProfileProvider) Get(userID string) (*Actor, error) {
userRoot := fmt.Sprintf("https://%s/users/%s", pp.domain, userID)
func (pp *ProfileProvider) Get() (*Actor, error) {
userRoot := fmt.Sprintf("https://%s/users/%s", pp.domain, pp.username)
return &Actor{
ID: userRoot,
Type: "Person",
URL: fmt.Sprintf("https://%s/@%s", pp.domain, userID),
Name: userID,
URL: fmt.Sprintf("https://%s/@%s", pp.domain, pp.username),
Name: pp.username,
Inbox: userRoot + "/inbox",
Outbox: userRoot + "/outbox",
Followers: userRoot + "/followers",

View File

@ -1,6 +1,9 @@
package main
import "time"
import (
"fmt"
"time"
)
type Stream struct {
Context string `json:"@context"`
@ -27,12 +30,33 @@ type Object struct {
Name string `json:"name"`
}
type Streams struct{}
type InMemoryStreams struct {
}
func (s *Streams) Inbox()
// view activities from other people directed to out user
func (s *InMemoryStreams) GetActivitiesToUser() (*Stream, error) {
return nil, fmt.Errorf("not implemented")
}
func (s *Streams) Outbox()
// view activities created by our user
func (s *InMemoryStreams) GetActivitiesFromUser() (*Stream, error) {
return nil, fmt.Errorf("not implemented")
}
func (s *Streams) Followers()
// insert activities from other people directed to our user
func (s *InMemoryStreams) AddActivityToUser() error {
return fmt.Errorf("not implemented")
}
func (s *Streams) Followed()
// insert activities from our user to be dispatched
func (s *InMemoryStreams) AddActivityFromUser() error {
return fmt.Errorf("not implemented")
}
func (s *InMemoryStreams) Followers() (*Stream, error) {
return nil, fmt.Errorf("not implemented")
}
func (s *InMemoryStreams) Followed() (*Stream, error) {
return nil, fmt.Errorf("not implemented")
}