mirror of
https://github.com/binwiederhier/ntfy.git
synced 2025-05-31 10:39:23 +02:00
Stuff
This commit is contained in:
parent
c35e5b33d1
commit
c2f16f740b
21 changed files with 332 additions and 547 deletions
server
127
server/server.go
127
server/server.go
|
@ -38,10 +38,7 @@ import (
|
|||
TODO
|
||||
expire tokens
|
||||
auto-refresh tokens from UI
|
||||
pricing page
|
||||
home page
|
||||
reserve topics
|
||||
|
||||
Pages:
|
||||
- Home
|
||||
- Signup
|
||||
|
@ -52,11 +49,6 @@ import (
|
|||
- change email
|
||||
-
|
||||
|
||||
Config flags:
|
||||
-
|
||||
- enable-register: true|false
|
||||
- enable-login: true|false
|
||||
- enable-reset-password: true|false
|
||||
|
||||
|
||||
*/
|
||||
|
@ -74,7 +66,7 @@ type Server struct {
|
|||
visitors map[string]*visitor // ip:<ip> or user:<user>
|
||||
firebaseClient *firebaseClient
|
||||
messages int64
|
||||
auth auth.Auther
|
||||
auth auth.Manager
|
||||
messageCache *messageCache
|
||||
fileCache *fileCache
|
||||
closeChan chan bool
|
||||
|
@ -96,18 +88,19 @@ var (
|
|||
authPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/auth$`)
|
||||
publishPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}/(publish|send|trigger)$`)
|
||||
|
||||
webConfigPath = "/config.js"
|
||||
userStatsPath = "/user/stats" // FIXME get rid of this in favor of /user/account
|
||||
userTokenPath = "/user/token"
|
||||
userAccountPath = "/user/account"
|
||||
userSubscriptionPath = "/user/subscription"
|
||||
userSubscriptionDeleteRegex = regexp.MustCompile(`^/user/subscription/([-_A-Za-z0-9]{16})$`)
|
||||
matrixPushPath = "/_matrix/push/v1/notify"
|
||||
staticRegex = regexp.MustCompile(`^/static/.+`)
|
||||
docsRegex = regexp.MustCompile(`^/docs(|/.*)$`)
|
||||
fileRegex = regexp.MustCompile(`^/file/([-_A-Za-z0-9]{1,64})(?:\.[A-Za-z0-9]{1,16})?$`)
|
||||
disallowedTopics = []string{"docs", "static", "file", "app", "settings"} // If updated, also update in Android app
|
||||
urlRegex = regexp.MustCompile(`^https?://`)
|
||||
webConfigPath = "/config.js"
|
||||
userStatsPath = "/user/stats" // FIXME get rid of this in favor of /user/account
|
||||
accountPath = "/v1/account"
|
||||
accountTokenPath = "/v1/account/token"
|
||||
accountSettingsPath = "/v1/account/settings"
|
||||
accountSubscriptionPath = "/v1/account/subscription"
|
||||
accountSubscriptionSingleRegex = regexp.MustCompile(`^/v1/account/subscription/([-_A-Za-z0-9]{16})$`)
|
||||
matrixPushPath = "/_matrix/push/v1/notify"
|
||||
staticRegex = regexp.MustCompile(`^/static/.+`)
|
||||
docsRegex = regexp.MustCompile(`^/docs(|/.*)$`)
|
||||
fileRegex = regexp.MustCompile(`^/file/([-_A-Za-z0-9]{1,64})(?:\.[A-Za-z0-9]{1,16})?$`)
|
||||
disallowedTopics = []string{"docs", "static", "file", "app", "settings"} // If updated, also update in Android app
|
||||
urlRegex = regexp.MustCompile(`^https?://`)
|
||||
|
||||
//go:embed site
|
||||
webFs embed.FS
|
||||
|
@ -160,9 +153,9 @@ func New(conf *Config) (*Server, error) {
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
var auther auth.Auther
|
||||
var auther auth.Manager
|
||||
if conf.AuthFile != "" {
|
||||
auther, err = auth.NewSQLiteAuth(conf.AuthFile, conf.AuthDefaultRead, conf.AuthDefaultWrite)
|
||||
auther, err = auth.NewSQLiteAuthManager(conf.AuthFile, conf.AuthDefaultRead, conf.AuthDefaultWrite)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -335,18 +328,20 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
|
|||
return s.ensureWebEnabled(s.handleWebConfig)(w, r, v)
|
||||
} else if r.Method == http.MethodGet && r.URL.Path == userStatsPath {
|
||||
return s.handleUserStats(w, r, v)
|
||||
} else if r.Method == http.MethodGet && r.URL.Path == userTokenPath {
|
||||
return s.handleUserTokenCreate(w, r, v)
|
||||
} else if r.Method == http.MethodDelete && r.URL.Path == userTokenPath {
|
||||
return s.handleUserTokenDelete(w, r, v)
|
||||
} else if r.Method == http.MethodGet && r.URL.Path == userAccountPath {
|
||||
return s.handleUserAccount(w, r, v)
|
||||
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && r.URL.Path == userAccountPath {
|
||||
return s.handleUserAccountUpdate(w, r, v)
|
||||
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && r.URL.Path == userSubscriptionPath {
|
||||
return s.handleUserSubscriptionAdd(w, r, v)
|
||||
} else if r.Method == http.MethodDelete && userSubscriptionDeleteRegex.MatchString(r.URL.Path) {
|
||||
return s.handleUserSubscriptionDelete(w, r, v)
|
||||
} else if r.Method == http.MethodPost && r.URL.Path == accountPath {
|
||||
return s.handleUserAccountCreate(w, r, v)
|
||||
} else if r.Method == http.MethodGet && r.URL.Path == accountTokenPath {
|
||||
return s.handleAccountTokenGet(w, r, v)
|
||||
} else if r.Method == http.MethodDelete && r.URL.Path == accountTokenPath {
|
||||
return s.handleAccountTokenDelete(w, r, v)
|
||||
} else if r.Method == http.MethodGet && r.URL.Path == accountSettingsPath {
|
||||
return s.handleAccountSettingsGet(w, r, v)
|
||||
} else if r.Method == http.MethodPost && r.URL.Path == accountSettingsPath {
|
||||
return s.handleAccountSettingsPost(w, r, v)
|
||||
} else if r.Method == http.MethodPost && r.URL.Path == accountSubscriptionPath {
|
||||
return s.handleAccountSubscriptionAdd(w, r, v)
|
||||
} else if r.Method == http.MethodDelete && accountSubscriptionSingleRegex.MatchString(r.URL.Path) {
|
||||
return s.handleAccountSubscriptionDelete(w, r, v)
|
||||
} else if r.Method == http.MethodGet && r.URL.Path == matrixPushPath {
|
||||
return s.handleMatrixDiscovery(w)
|
||||
} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) {
|
||||
|
@ -441,11 +436,7 @@ func (s *Server) handleUserStats(w http.ResponseWriter, r *http.Request, v *visi
|
|||
return nil
|
||||
}
|
||||
|
||||
type tokenAuthResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func (s *Server) handleUserTokenCreate(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
func (s *Server) handleAccountTokenGet(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
// TODO rate limit
|
||||
if v.user == nil {
|
||||
return errHTTPUnauthorized
|
||||
|
@ -456,7 +447,7 @@ func (s *Server) handleUserTokenCreate(w http.ResponseWriter, r *http.Request, v
|
|||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
|
||||
response := &tokenAuthResponse{
|
||||
response := &apiAccountTokenResponse{
|
||||
Token: token,
|
||||
}
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
|
@ -465,7 +456,7 @@ func (s *Server) handleUserTokenCreate(w http.ResponseWriter, r *http.Request, v
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleUserTokenDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
func (s *Server) handleAccountTokenDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
// TODO rate limit
|
||||
if v.user == nil || v.user.Token == "" {
|
||||
return errHTTPUnauthorized
|
||||
|
@ -477,24 +468,10 @@ func (s *Server) handleUserTokenDelete(w http.ResponseWriter, r *http.Request, v
|
|||
return nil
|
||||
}
|
||||
|
||||
type userPlanResponse struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type userAccountResponse struct {
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role,omitempty"`
|
||||
Plan *userPlanResponse `json:"plan,omitempty"`
|
||||
Language string `json:"language,omitempty"`
|
||||
Notification *auth.UserNotificationPrefs `json:"notification,omitempty"`
|
||||
Subscriptions []*auth.UserSubscription `json:"subscriptions,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Server) handleUserAccount(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
func (s *Server) handleAccountSettingsGet(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
|
||||
response := &userAccountResponse{}
|
||||
response := &apiAccountSettingsResponse{}
|
||||
if v.user != nil {
|
||||
response.Username = v.user.Name
|
||||
response.Role = string(v.user.Role)
|
||||
|
@ -510,7 +487,7 @@ func (s *Server) handleUserAccount(w http.ResponseWriter, r *http.Request, v *vi
|
|||
}
|
||||
}
|
||||
} else {
|
||||
response = &userAccountResponse{
|
||||
response = &apiAccountSettingsResponse{
|
||||
Username: auth.Everyone,
|
||||
Role: string(auth.RoleAnonymous),
|
||||
}
|
||||
|
@ -521,7 +498,31 @@ func (s *Server) handleUserAccount(w http.ResponseWriter, r *http.Request, v *vi
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleUserAccountUpdate(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
func (s *Server) handleUserAccountCreate(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
signupAllowed := s.config.EnableSignup
|
||||
admin := v.user != nil && v.user.Role == auth.RoleAdmin
|
||||
if !signupAllowed && !admin {
|
||||
return errHTTPUnauthorized
|
||||
}
|
||||
body, err := util.Peek(r.Body, 4096) // FIXME
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
var newAccount apiAccountCreateRequest
|
||||
if err := json.NewDecoder(body).Decode(&newAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.auth.AddUser(newAccount.Username, newAccount.Password, auth.RoleUser); err != nil { // TODO this should return a User
|
||||
return err
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
|
||||
// FIXME return something
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleAccountSettingsPost(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
if v.user == nil {
|
||||
return errors.New("no user")
|
||||
}
|
||||
|
@ -560,7 +561,7 @@ func (s *Server) handleUserAccountUpdate(w http.ResponseWriter, r *http.Request,
|
|||
return s.auth.ChangeSettings(v.user)
|
||||
}
|
||||
|
||||
func (s *Server) handleUserSubscriptionAdd(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
func (s *Server) handleAccountSubscriptionAdd(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
if v.user == nil {
|
||||
return errors.New("no user")
|
||||
}
|
||||
|
@ -598,13 +599,13 @@ func (s *Server) handleUserSubscriptionAdd(w http.ResponseWriter, r *http.Reques
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleUserSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
func (s *Server) handleAccountSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
if v.user == nil {
|
||||
return errors.New("no user")
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // FIXME remove this
|
||||
matches := userSubscriptionDeleteRegex.FindStringSubmatch(r.URL.Path)
|
||||
matches := accountSubscriptionSingleRegex.FindStringSubmatch(r.URL.Path)
|
||||
if len(matches) != 2 {
|
||||
return errHTTPInternalErrorInvalidFilePath // FIXME
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue