-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathapi.go
136 lines (118 loc) · 2.69 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package api
import (
"encoding/json"
"errors"
"math/rand"
"net/http"
"github.com/apex/log"
"github.com/fatih/set"
"github.com/gorilla/mux"
"github.com/tympanix/supper/app/notify"
"github.com/tympanix/supper/types"
"golang.org/x/text/language"
)
// API exposes endpoints for the webapp to perform HTTP RESTFull actions
type API struct {
types.App
*Hub
*mux.Router
}
// Error is an error occurring in an API endpoint
type Error interface {
error
Status() int
}
// New creates a new API handler
func New(app types.App) http.Handler {
api := &API{
App: app,
Hub: newHub(),
Router: mux.NewRouter(),
}
api.Handle("/media", apiHandler(api.media))
api.Handle("/config", apiHandler(api.config))
api.HandleFunc("/ws", api.serveWebsocket)
apiSubs := api.PathPrefix("/subtitles").Subrouter()
api.subtitleRouter(apiSubs)
go api.Hub.run()
return api
}
func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.Router.ServeHTTP(w, r)
}
type apiError struct {
error `json:"-"`
Code int `json:"-"`
}
func (e *apiError) Status() int {
return e.Code
}
func (e *apiError) MarshalJSON() (b []byte, err error) {
return json.Marshal(struct {
Error string `json:"error"`
}{
Error: e.Error(),
})
}
func (a *API) queryLang(r *http.Request) (set.Interface, error) {
v := r.URL.Query()
lang := v.Get("lang")
if lang == "" {
return a.Config().Languages(), nil
}
l := language.Make(lang)
if l == language.Und {
return set.New(), errors.New("unknown language")
}
return set.New(l), nil
}
type apiHandler func(http.ResponseWriter, *http.Request) interface{}
func (fn apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r); e != nil {
if err, ok := e.(error); ok {
e = fn.handleError(w, err)
}
js, err := json.MarshalIndent(e, "", " ")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
} else {
http.Error(w, "Not found", http.StatusNotFound)
}
}
func (fn apiHandler) handleError(w http.ResponseWriter, err error) error {
var apiError Error
if e, ok := err.(Error); ok {
apiError = e
} else {
apiError = NewError(err, http.StatusBadRequest)
}
w.WriteHeader(apiError.Status())
return apiError
}
// NewError returns a new error
func NewError(err error, status int) Error {
return &apiError{
err,
status,
}
}
func (a *API) asyncSendToWebsocket() chan<- *notify.Entry {
c := make(chan *notify.Entry)
job := rand.Uint32()
go func() {
for v := range c {
v.Context = v.Context.WithField("job", job)
data, err := json.Marshal(v)
if err != nil {
log.WithError(err).Error("Websocket error")
continue
}
a.Broadcast(data)
}
}()
return c
}