Refactor a bunch!
This commit is contained in:
parent
d6b0c343f7
commit
ea68376bf9
3 changed files with 210 additions and 153 deletions
75
data.go
Normal file
75
data.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type appDef struct {
|
||||||
|
Name string
|
||||||
|
WebhookURL string
|
||||||
|
PublicKey *rsa.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
var apps map[string]appDef
|
||||||
|
|
||||||
|
type profileLink struct {
|
||||||
|
Rel string `json:"rel"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Href string `json:"href"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type profileWebFingerData struct {
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Links []profileLink `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type pubKeyBlock struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
PEM string `json:"publicKeyPem"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type profilePage struct {
|
||||||
|
Context []string `json:"@context"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
PreferredUsername string `json:"preferredUsername"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Inbox string `json:"inbox"`
|
||||||
|
Followers string `json:"followers"`
|
||||||
|
PublicKey *pubKeyBlock `json:"publicKey,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type inboxPush struct {
|
||||||
|
Actor string `json:"actor"`
|
||||||
|
CC []string `json:"cc"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Object interface{} `json:"object"`
|
||||||
|
To []string `json:"to"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Context string `json:"context,omitempty"`
|
||||||
|
Published time.Time `json:"published,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type inboxCreatePayload struct {
|
||||||
|
Actor string `json:"actor"`
|
||||||
|
AttributedTo string `json:"attributedTo"`
|
||||||
|
CC []string `json:"cc"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Context string `json:"context"`
|
||||||
|
Conversation string `json:"conversation"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Published time.Time `json:"published"`
|
||||||
|
Sensitive bool `json:"sensitive"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Tag []tag `json:"tag"`
|
||||||
|
To []string `json:"to"`
|
||||||
|
Type string `json:"Type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type tag struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
115
handlers.go
Normal file
115
handlers.go
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hostMetaHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
headers := w.Header()
|
||||||
|
headers.Add("Content-Type", "application/xrd+xml")
|
||||||
|
headers.Add("Content-Type", "charset=utf-8")
|
||||||
|
fmt.Fprintf(w, `<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="https://%s/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>`, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerUserPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
parts := strings.SplitN(strings.Trim(r.URL.Path, " /"), "/", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
http.Error(w, "page not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := parts[1]
|
||||||
|
app, ok := apps[name]
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "page not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userURL := fmt.Sprintf("%s/u/%s", fullhost, name)
|
||||||
|
|
||||||
|
contextes := []string{
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubkey *pubKeyBlock = nil
|
||||||
|
if app.PublicKey != nil {
|
||||||
|
pubKeyBytes := x509.MarshalPKCS1PublicKey(app.PublicKey)
|
||||||
|
pubKeyPem := string(pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "PUBLIC KEY",
|
||||||
|
Bytes: pubKeyBytes,
|
||||||
|
}))
|
||||||
|
contextes = append(contextes, "https://w3id.org/security/v1")
|
||||||
|
pubkey = &pubKeyBlock{
|
||||||
|
ID: userURL + "#main-key",
|
||||||
|
Owner: userURL,
|
||||||
|
PEM: pubKeyPem,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers := w.Header()
|
||||||
|
headers.Add("Content-Type", "application/activity+json")
|
||||||
|
headers.Add("Content-Type", "charset=utf-8")
|
||||||
|
err := json.NewEncoder(w).Encode(profilePage{
|
||||||
|
Context: contextes,
|
||||||
|
ID: userURL,
|
||||||
|
Type: "Person",
|
||||||
|
PreferredUsername: name,
|
||||||
|
Name: app.Name,
|
||||||
|
Inbox: fmt.Sprintf("%s/inbox", fullhost),
|
||||||
|
Followers: fmt.Sprintf("%s/followers", userURL),
|
||||||
|
PublicKey: pubkey,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Error encoding page: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerWebFinger(w http.ResponseWriter, r *http.Request) {
|
||||||
|
params := r.URL.Query()
|
||||||
|
resource := params.Get("resource")
|
||||||
|
if resource == "" {
|
||||||
|
http.Error(w, "must include a valid resource", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(resource, ":", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
http.Error(w, "invalid resource specified", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch parts[0] {
|
||||||
|
case "acct":
|
||||||
|
// Split user by host
|
||||||
|
userParts := strings.SplitN(parts[1], "@", 2)
|
||||||
|
if len(userParts) < 2 || strings.ToLower(userParts[1]) != host {
|
||||||
|
http.Error(w, "Invalid user specified", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check if user is a registered app
|
||||||
|
_, ok := apps[userParts[0]]
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "user not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
headers := w.Header()
|
||||||
|
headers.Add("Content-Type", "application/json")
|
||||||
|
headers.Add("Content-Type", "charset=utf-8")
|
||||||
|
|
||||||
|
err := json.NewEncoder(w).Encode(profileWebFingerData{
|
||||||
|
Subject: resource,
|
||||||
|
Links: []profileLink{
|
||||||
|
{
|
||||||
|
Rel: "self",
|
||||||
|
Type: "application/activity+json",
|
||||||
|
Href: fmt.Sprintf("%s/u/%s", fullhost, userParts[0]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Error encoding webfinger: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
main.go
173
main.go
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -23,45 +22,11 @@ func mustGetEnv(env string) (val string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var host string
|
var host string
|
||||||
|
var fullhost string
|
||||||
type appDef struct {
|
var prefix *string
|
||||||
Name string
|
|
||||||
WebhookURL string
|
|
||||||
PublicKey *rsa.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
var apps map[string]appDef
|
|
||||||
|
|
||||||
type profileLink struct {
|
|
||||||
Rel string `json:"rel"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Href string `json:"href"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type profileWebFingerData struct {
|
|
||||||
Subject string `json:"subject"`
|
|
||||||
Links []profileLink `json:"links"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type pubKeyBlock struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Owner string `json:"owner"`
|
|
||||||
PEM string `json:"publicKeyPem"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type profilePage struct {
|
|
||||||
Context []string `json:"@context"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
PreferredUsername string `json:"preferredUsername"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Inbox string `json:"inbox"`
|
|
||||||
Followers string `json:"followers"`
|
|
||||||
PublicKey *pubKeyBlock `json:"publicKey,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
prefix := flag.String("prefix", "AW_", "Environment var prefix")
|
prefix = flag.String("prefix", "AW_", "Environment var prefix")
|
||||||
proto := flag.String("proto", "https", "Protocol under which this server is going to be exposed")
|
proto := flag.String("proto", "https", "Protocol under which this server is going to be exposed")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -71,8 +36,24 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
host = strings.ToLower(mustGetEnv(*prefix + "HOST"))
|
host = strings.ToLower(mustGetEnv(*prefix + "HOST"))
|
||||||
fullhost := fmt.Sprintf("%s://%s", *proto, host)
|
fullhost = fmt.Sprintf("%s://%s", *proto, host)
|
||||||
|
|
||||||
|
discoverApps()
|
||||||
|
|
||||||
|
http.HandleFunc("/.well-known/host-meta", hostMetaHandler)
|
||||||
|
http.HandleFunc("/.well-known/webfinger", handlerWebFinger)
|
||||||
|
http.HandleFunc("/u/", handlerUserPage)
|
||||||
|
http.HandleFunc("/inbox", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
byt, _ := ioutil.ReadAll(r.Body)
|
||||||
|
fmt.Println(string(byt))
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := http.ListenAndServe(bind, nil); err != nil {
|
||||||
|
log.Fatalf("error while listening: %f", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func discoverApps() {
|
||||||
// Find app definitions
|
// Find app definitions
|
||||||
envs := os.Environ()
|
envs := os.Environ()
|
||||||
envAppPrefix := *prefix + "APP_"
|
envAppPrefix := *prefix + "APP_"
|
||||||
|
@ -113,118 +94,4 @@ func main() {
|
||||||
|
|
||||||
log.Printf("app registered: %s", appName)
|
log.Printf("app registered: %s", appName)
|
||||||
}
|
}
|
||||||
|
|
||||||
http.HandleFunc("/.well-known/host-meta", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
headers := w.Header()
|
|
||||||
headers.Add("Content-Type", "application/xrd+xml")
|
|
||||||
headers.Add("Content-Type", "charset=utf-8")
|
|
||||||
fmt.Fprintf(w, `<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="https://%s/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>`, host)
|
|
||||||
})
|
|
||||||
|
|
||||||
http.HandleFunc("/.well-known/webfinger", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
params := r.URL.Query()
|
|
||||||
resource := params.Get("resource")
|
|
||||||
if resource == "" {
|
|
||||||
http.Error(w, "must include a valid resource", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := strings.SplitN(resource, ":", 2)
|
|
||||||
if len(parts) < 2 {
|
|
||||||
http.Error(w, "invalid resource specified", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch parts[0] {
|
|
||||||
case "acct":
|
|
||||||
// Split user by host
|
|
||||||
userParts := strings.SplitN(parts[1], "@", 2)
|
|
||||||
if len(userParts) < 2 || strings.ToLower(userParts[1]) != host {
|
|
||||||
http.Error(w, "Invalid user specified", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Check if user is a registered app
|
|
||||||
_, ok := apps[userParts[0]]
|
|
||||||
if !ok {
|
|
||||||
http.Error(w, "user not found", http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
headers := w.Header()
|
|
||||||
headers.Add("Content-Type", "application/json")
|
|
||||||
headers.Add("Content-Type", "charset=utf-8")
|
|
||||||
|
|
||||||
err := json.NewEncoder(w).Encode(profileWebFingerData{
|
|
||||||
Subject: resource,
|
|
||||||
Links: []profileLink{
|
|
||||||
{
|
|
||||||
Rel: "self",
|
|
||||||
Type: "application/activity+json",
|
|
||||||
Href: fmt.Sprintf("%s/u/%s", fullhost, userParts[0]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Error encoding webfinger: "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
http.HandleFunc("/u/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
parts := strings.SplitN(strings.Trim(r.URL.Path, " /"), "/", 2)
|
|
||||||
if len(parts) < 2 {
|
|
||||||
http.Error(w, "page not found", http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := parts[1]
|
|
||||||
app, ok := apps[name]
|
|
||||||
if !ok {
|
|
||||||
http.Error(w, "page not found", http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userURL := fmt.Sprintf("%s/u/%s", fullhost, name)
|
|
||||||
|
|
||||||
contextes := []string{
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubkey *pubKeyBlock = nil
|
|
||||||
if app.PublicKey != nil {
|
|
||||||
pubKeyBytes := x509.MarshalPKCS1PublicKey(app.PublicKey)
|
|
||||||
pubKeyPem := string(pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "PUBLIC KEY",
|
|
||||||
Bytes: pubKeyBytes,
|
|
||||||
}))
|
|
||||||
contextes = append(contextes, "https://w3id.org/security/v1")
|
|
||||||
pubkey = &pubKeyBlock{
|
|
||||||
ID: userURL + "#main-key",
|
|
||||||
Owner: userURL,
|
|
||||||
PEM: pubKeyPem,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
headers := w.Header()
|
|
||||||
headers.Add("Content-Type", "application/activity+json")
|
|
||||||
headers.Add("Content-Type", "charset=utf-8")
|
|
||||||
err := json.NewEncoder(w).Encode(profilePage{
|
|
||||||
Context: contextes,
|
|
||||||
ID: userURL,
|
|
||||||
Type: "Person",
|
|
||||||
PreferredUsername: name,
|
|
||||||
Name: app.Name,
|
|
||||||
Inbox: fmt.Sprintf("%s/inbox", fullhost),
|
|
||||||
Followers: fmt.Sprintf("%s/followers", userURL),
|
|
||||||
PublicKey: pubkey,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Error encoding page: "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
http.HandleFunc("/inbox", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
byt, _ := ioutil.ReadAll(r.Body)
|
|
||||||
fmt.Println(string(byt))
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := http.ListenAndServe(bind, nil); err != nil {
|
|
||||||
log.Fatalf("error while listening: %f", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue