diff --git a/main.go b/main.go index 2e2a35c..e7078ad 100644 --- a/main.go +++ b/main.go @@ -25,15 +25,19 @@ func main() { // API endpoints mux.Group(func(mux *flow.Mux) { mux.Use(handlers.Cors) - mux.HandleFunc("/api/v1/invite/:id", handlers.GetInvite, "GET", "DELETE") + mux.HandleFunc("/api/v1/invite/:id", handlers.GetInvite, "GET") mux.Use(handlers.SessionAuth) mux.HandleFunc("/api/v1/me", handlers.CurrentUser, "GET") mux.HandleFunc("/api/v1/invites", handlers.CreateInvite, "POST") mux.HandleFunc("/api/v1/invite/:id/accept", handlers.AcceptInvite, "POST") + mux.HandleFunc("/api/v1/invite/:id/log", handlers.InviteLog, "GET") + mux.HandleFunc("/api/v1/invite/:id", handlers.DeleteInvite, "DELETE") - mux.HandleFunc("/api/v1/servers", handlers.Server, "GET", "POST") + mux.HandleFunc("/api/v1/servers", handlers.Servers, "GET", "POST") + mux.HandleFunc("/api/v1/server/:id", handlers.Server, "GET", "DELETE") + mux.HandleFunc("/api/v1/server/:id/invites", handlers.ServerInvites, "GET") }) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { diff --git a/store/database.go b/store/database.go index 4175c00..a507d12 100644 --- a/store/database.go +++ b/store/database.go @@ -26,8 +26,10 @@ type Storer interface { GetServer(id string) (Server, error) SaveServer(server Server) error GetUserServers(user User) ([]Server, error) + DeleteServer(server Server, user User) error LogInviteUse(user string, invite Invite) error InviteLog(invite Invite) ([]InviteLog, error) + DeleteInvite(invite Invite) error GetUser(uid string) (User, error) SaveUser(user User) error SaveSession(token string, user User) error @@ -35,6 +37,7 @@ type Storer interface { SaveOauthState(state OauthState) error OauthState(id string) (OauthState, error) Close() error + ServerInvites(server Server) ([]Invite, error) } type OauthState struct { @@ -78,6 +81,12 @@ type Session struct { Expiry time.Time } +type InviteLog struct { + EntryID string `json:"entry_id"` + Invite Invite `json:"invite"` + User User `json:"user"` +} + func Open() (*Store, error) { database := os.Getenv("WLM_DATABASE_PATH") if database == "" { @@ -165,12 +174,6 @@ func (s *Store) LogInviteUse(user string, invite Invite) error { return err } -type InviteLog struct { - EntryID string - Invite string - User string -} - func (s *Store) InviteLog(invite Invite) ([]InviteLog, error) { q, err := s.database.Query("SELECT * FROM invite_log WHERE invite=$1", invite.Token) if err != nil { @@ -180,10 +183,15 @@ func (s *Store) InviteLog(invite Invite) ([]InviteLog, error) { var log []InviteLog for q.Next() { var logEntry InviteLog - err := q.Scan(&logEntry.EntryID, &logEntry.Invite, &logEntry.User) + err := q.Scan(&logEntry.EntryID, &logEntry.Invite.Token, &logEntry.User.Id) if err != nil { continue } + user, err := s.GetUser(logEntry.User.Id) + if err != nil { + continue + } + logEntry.User = user log = append(log, logEntry) } @@ -354,3 +362,62 @@ func (s *Store) OauthState(id string) (OauthState, error) { func (s *Store) Close() error { return s.database.Close() } + +func (s *Store) ServerInvites(server Server) ([]Invite, error) { + q, err := s.database.Query("SELECT * FROM invites WHERE server=$1", server.Id) + if err != nil { + return []Invite{}, err + } + var invites []Invite + for q.Next() { + var invite Invite + err = q.Scan(&invite.Token, &invite.Creator.Id, &invite.Server.Id, &invite.Uses, &invite.Unlimited) + if err != nil { + continue + } + invites = append(invites, invite) + } + + return invites, nil +} + +func (s *Store) DeleteInvite(invite Invite) error { + inviteDeleteQuery, err := s.database.Prepare("DELETE FROM invites WHERE token=$1") + if err != nil { + return err + } + _, err = s.database.Exec("DELETE FROM invite_log WHERE invite=$1", invite.Token) + if err != nil { + return err + } + + _, err = inviteDeleteQuery.Exec(invite.Token) + if err != nil { + return err + } + + return nil +} + +func (s *Store) DeleteServer(server Server, user User) error { + serverDeleteQuery, err := s.database.Prepare("DELETE FROM servers WHERE id=$1 AND owner=$2") + if err != nil { + return err + } + serverInvites, err := s.ServerInvites(server) + if err != nil { + return err + } + for _, invite := range serverInvites { + err = s.DeleteInvite(invite) + if err != nil { + continue + } + } + _, err = serverDeleteQuery.Exec(server.Id, user.Id) + if err != nil { + return err + } + + return nil +} diff --git a/transport/http.go b/transport/http.go index fcc297b..5028590 100644 --- a/transport/http.go +++ b/transport/http.go @@ -29,10 +29,16 @@ type Handle interface { CurrentUser(w http.ResponseWriter, r *http.Request) CreateInvite(w http.ResponseWriter, r *http.Request) GetInvite(w http.ResponseWriter, r *http.Request) + DeleteInvite(w http.ResponseWriter, r *http.Request) AcceptInvite(w http.ResponseWriter, r *http.Request) AuthRedirect(w http.ResponseWriter, r *http.Request) AuthCallback(w http.ResponseWriter, r *http.Request) + Server(w http.ResponseWriter, r *http.Request) + Servers(w http.ResponseWriter, r *http.Request) CreateServer(w http.ResponseWriter, r *http.Request) + DeleteServer(w http.ResponseWriter, r *http.Request) + ServerInvites(w http.ResponseWriter, r *http.Request) + InviteLog(w http.ResponseWriter, r *http.Request) } func New(store store.Storer) *Handler { @@ -239,7 +245,7 @@ func generateSessionToken() (string, error) { return base64.URLEncoding.EncodeToString(b), err } -func (h *Handler) Server(w http.ResponseWriter, r *http.Request) { +func (h *Handler) Servers(w http.ResponseWriter, r *http.Request) { user := r.Context().Value("user").(store.User) if r.Method == http.MethodGet { servers, err := h.store.GetUserServers(user) @@ -268,6 +274,49 @@ func (h *Handler) Server(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "created server %s", server.Id) } +func (h *Handler) Server(w http.ResponseWriter, r *http.Request) { + user := r.Context().Value("user").(store.User) + serverId := flow.Param(r.Context(), "id") + if r.Method == http.MethodGet { + server, err := h.store.GetServer(serverId) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if server.Owner.Id != user.Id { + http.Error(w, "not owner of server", http.StatusForbidden) + return + } + + json.NewEncoder(w).Encode(server) + return + } + + if r.Method == http.MethodDelete { + serverId := flow.Param(r.Context(), "id") + user := r.Context().Value("user").(store.User) + server, err := h.store.GetServer(serverId) + if err != nil { + http.Error(w, "no such server", http.StatusNotFound) + return + } + + if server.Owner.Id != user.Id { + http.Error(w, "user not server owner", http.StatusForbidden) + return + } + + err = h.store.DeleteServer(server, user) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Write([]byte("deleted")) + } + +} + func (h *Handler) CurrentUser(w http.ResponseWriter, r *http.Request) { value := r.Context().Value("user") if value == nil { @@ -277,3 +326,72 @@ func (h *Handler) CurrentUser(w http.ResponseWriter, r *http.Request) { user := value.(store.User) w.Write([]byte(user.DisplayName)) } + +func (h *Handler) ServerInvites(w http.ResponseWriter, r *http.Request) { + user := r.Context().Value("user").(store.User) + serverId := flow.Param(r.Context(), "id") + server, err := h.store.GetServer(serverId) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if server.Owner.Id != user.Id { + http.Error(w, "not owner of server", http.StatusForbidden) + return + } + + serverInvites, err := h.store.ServerInvites(server) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if server.Owner.Id != user.Id { + http.Error(w, "not owner of server", http.StatusForbidden) + return + } + + json.NewEncoder(w).Encode(serverInvites) + return +} + +func (h *Handler) InviteLog(w http.ResponseWriter, r *http.Request) { + user := r.Context().Value("user").(store.User) + inviteToken := flow.Param(r.Context(), "id") + invite, err := h.store.GetInvite(inviteToken) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if invite.Creator.Id != user.Id { + http.Error(w, "not owner of invite", http.StatusForbidden) + return + } + logs, err := h.store.InviteLog(invite) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + json.NewEncoder(w).Encode(logs) +} + +func (h *Handler) DeleteInvite(w http.ResponseWriter, r *http.Request) { + user := r.Context().Value("user").(store.User) + inviteToken := flow.Param(r.Context(), "id") + invite, err := h.store.GetInvite(inviteToken) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if invite.Creator.Id != user.Id { + http.Error(w, "not owner of invite", http.StatusForbidden) + return + } + + err = h.store.DeleteInvite(invite) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write([]byte("deleted")) + +}