New structure (#7)
This commit is contained in:
81
pkg/routes/api/v1/list_by_namespace.go
Normal file
81
pkg/routes/api/v1/list_by_namespace.go
Normal file
@ -0,0 +1,81 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// GetListsByNamespaceID is the web handler to delete a namespace
|
||||
func GetListsByNamespaceID(c echo.Context) error {
|
||||
// swagger:operation GET /namespaces/{namespaceID}/lists namespaces getListsByNamespace
|
||||
// ---
|
||||
// summary: gets all lists belonging to that namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Namespace"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// Get our namespace
|
||||
namespace, err := getNamespace(c)
|
||||
if err != nil {
|
||||
if models.IsErrNamespaceDoesNotExist(err) {
|
||||
return c.JSON(http.StatusNotFound, models.Message{"Namespace not found."})
|
||||
}
|
||||
if models.IsErrUserDoesNotHaveAccessToNamespace(err) {
|
||||
return c.JSON(http.StatusForbidden, models.Message{"You don't have access to this namespace."})
|
||||
}
|
||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occurred."})
|
||||
}
|
||||
|
||||
// Get the lists
|
||||
lists, err := models.GetListsByNamespaceID(namespace.ID)
|
||||
if err != nil {
|
||||
if models.IsErrNamespaceDoesNotExist(err) {
|
||||
return c.JSON(http.StatusNotFound, models.Message{"Namespace not found."})
|
||||
}
|
||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occurred."})
|
||||
}
|
||||
return c.JSON(http.StatusOK, lists)
|
||||
}
|
||||
|
||||
func getNamespace(c echo.Context) (namespace models.Namespace, err error) {
|
||||
// Check if we have our ID
|
||||
id := c.Param("namespace")
|
||||
// Make int
|
||||
namespaceID, err := strconv.ParseInt(id, 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the namespace
|
||||
namespace, err = models.GetNamespaceByID(namespaceID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user has acces to that namespace
|
||||
user, err := models.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !namespace.CanRead(&user) {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
69
pkg/routes/api/v1/login.go
Normal file
69
pkg/routes/api/v1/login.go
Normal file
@ -0,0 +1,69 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/spf13/viper"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Login is the login handler
|
||||
func Login(c echo.Context) error {
|
||||
// swagger:operation POST /login user login
|
||||
// ---
|
||||
// summary: Logs a user in. Returns a JWT-Token to authenticate requests
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UserLogin"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Token"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
u := models.UserLogin{}
|
||||
if err := c.Bind(&u); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.Message{"Please provide a username and password."})
|
||||
}
|
||||
|
||||
// Check user
|
||||
user, err := models.CheckUserCredentials(&u)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusUnauthorized, models.Message{"Wrong username or password."})
|
||||
}
|
||||
|
||||
// Create token
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
|
||||
// Set claims
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
claims["username"] = user.Username
|
||||
claims["email"] = user.Email
|
||||
claims["id"] = user.ID
|
||||
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
||||
|
||||
avatar := md5.Sum([]byte(user.Email))
|
||||
claims["avatar"] = hex.EncodeToString(avatar[:])
|
||||
|
||||
// Generate encoded token and send it as response.
|
||||
t, err := token.SignedString([]byte(viper.GetString("service.JWTSecret")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, map[string]string{
|
||||
"token": t,
|
||||
})
|
||||
}
|
54
pkg/routes/api/v1/swagger/options.go
Normal file
54
pkg/routes/api/v1/swagger/options.go
Normal file
@ -0,0 +1,54 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
)
|
||||
|
||||
// not actually a response, just a hack to get go-swagger to include definitions
|
||||
// of the various XYZOption structs
|
||||
|
||||
// parameterBodies
|
||||
// swagger:response parameterBodies
|
||||
type swaggerParameterBodies struct {
|
||||
// in:body
|
||||
UserLogin models.UserLogin
|
||||
|
||||
// in:body
|
||||
APIUserPassword models.APIUserPassword
|
||||
|
||||
// in:body
|
||||
List models.List
|
||||
|
||||
// in:body
|
||||
ListTask models.ListTask
|
||||
|
||||
// in:body
|
||||
Namespace models.Namespace
|
||||
|
||||
// in:body
|
||||
Team models.Team
|
||||
|
||||
// in:body
|
||||
TeamMember models.TeamMember
|
||||
|
||||
// in:body
|
||||
TeamList models.TeamList
|
||||
|
||||
// in:body
|
||||
TeamNamespace models.TeamNamespace
|
||||
|
||||
// in:body
|
||||
ListUser models.ListUser
|
||||
|
||||
// in:body
|
||||
NamespaceUser models.NamespaceUser
|
||||
|
||||
// in:body
|
||||
PasswordReset models.PasswordReset
|
||||
|
||||
// in:body
|
||||
PasswordTokenRequest models.PasswordTokenRequest
|
||||
|
||||
// in:body
|
||||
EmailConfirm models.EmailConfirm
|
||||
}
|
111
pkg/routes/api/v1/swagger/responses.go
Normal file
111
pkg/routes/api/v1/swagger/responses.go
Normal file
@ -0,0 +1,111 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
)
|
||||
|
||||
// Message
|
||||
// swagger:response Message
|
||||
type swaggerResponseMessage struct {
|
||||
// in:body
|
||||
Body models.Message `json:"body"`
|
||||
}
|
||||
|
||||
// ================
|
||||
// User definitions
|
||||
// ================
|
||||
|
||||
// User Object
|
||||
// swagger:response User
|
||||
type swaggerResponseUser struct {
|
||||
// in:body
|
||||
Body models.User `json:"body"`
|
||||
}
|
||||
|
||||
// Token
|
||||
// swagger:response Token
|
||||
type swaggerResponseToken struct {
|
||||
// The body message
|
||||
// in:body
|
||||
Body struct {
|
||||
// The token
|
||||
//
|
||||
// Required: true
|
||||
Token string `json:"token"`
|
||||
} `json:"body"`
|
||||
}
|
||||
|
||||
// ================
|
||||
// List definitions
|
||||
// ================
|
||||
|
||||
// List
|
||||
// swagger:response List
|
||||
type swaggerResponseLIst struct {
|
||||
// in:body
|
||||
Body models.List `json:"body"`
|
||||
}
|
||||
|
||||
// ListTask
|
||||
// swagger:response ListTask
|
||||
type swaggerResponseLIstTask struct {
|
||||
// in:body
|
||||
Body models.ListTask `json:"body"`
|
||||
}
|
||||
|
||||
// ================
|
||||
// Namespace definitions
|
||||
// ================
|
||||
|
||||
// Namespace
|
||||
// swagger:response Namespace
|
||||
type swaggerResponseNamespace struct {
|
||||
// in:body
|
||||
Body models.Namespace `json:"body"`
|
||||
}
|
||||
|
||||
// ================
|
||||
// Team definitions
|
||||
// ================
|
||||
|
||||
// Team
|
||||
// swagger:response Team
|
||||
type swaggerResponseTeam struct {
|
||||
// in:body
|
||||
Body models.Team `json:"body"`
|
||||
}
|
||||
|
||||
// TeamMember
|
||||
// swagger:response TeamMember
|
||||
type swaggerResponseTeamMember struct {
|
||||
// in:body
|
||||
Body models.TeamMember `json:"body"`
|
||||
}
|
||||
|
||||
// TeamList
|
||||
// swagger:response TeamList
|
||||
type swaggerResponseTeamList struct {
|
||||
// in:body
|
||||
Body models.TeamList `json:"body"`
|
||||
}
|
||||
|
||||
// TeamNamespace
|
||||
// swagger:response TeamNamespace
|
||||
type swaggerResponseTeamNamespace struct {
|
||||
// in:body
|
||||
Body models.TeamNamespace `json:"body"`
|
||||
}
|
||||
|
||||
// UserList
|
||||
// swagger:response UserList
|
||||
type swaggerResponseUserList struct {
|
||||
// in:body
|
||||
Body models.ListUser `json:"body"`
|
||||
}
|
||||
|
||||
// UserNamespace
|
||||
// swagger:response UserNamespace
|
||||
type swaggerResponseUserNamespace struct {
|
||||
// in:body
|
||||
Body models.NamespaceUser `json:"body"`
|
||||
}
|
915
pkg/routes/api/v1/swaggerdocs.go
Normal file
915
pkg/routes/api/v1/swaggerdocs.go
Normal file
@ -0,0 +1,915 @@
|
||||
package v1
|
||||
|
||||
// swagger:operation DELETE /tasks/{taskID} lists deleteListTask
|
||||
// ---
|
||||
// summary: Deletes a list task
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: taskID
|
||||
// in: path
|
||||
// description: ID of the list task to delete
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "404":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /lists/{listID} lists deleteList
|
||||
// ---
|
||||
// summary: Deletes a list with all tasks on it
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to delete
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "404":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /lists/{listID} lists addListTask
|
||||
// ---
|
||||
// summary: Adds an task to a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ListTask"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ListTask"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /tasks/{taskID} lists updateListTask
|
||||
// ---
|
||||
// summary: Updates a list task
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: taskID
|
||||
// in: path
|
||||
// description: ID of the task to update
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ListTask"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ListTask"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /lists/{listID} lists getList
|
||||
// ---
|
||||
// summary: gets one list with all todo tasks
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to show
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/List"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /namespaces/{namespaceID}/lists lists addList
|
||||
// ---
|
||||
// summary: Creates a new list owned by the currently logged in user in that namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace that list should belong to
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/List"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/List"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /lists/{listID} lists upadteList
|
||||
// ---
|
||||
// summary: Updates a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to update
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/List"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/List"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /lists lists getLists
|
||||
// ---
|
||||
// summary: Gets all lists owned by the current user
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/List"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /namespaces namespaces addNamespace
|
||||
// ---
|
||||
// summary: Creates a new namespace owned by the currently logged in user
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/Namespace"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Namespace"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /namespaces/{namespaceID} namespaces upadteNamespace
|
||||
// ---
|
||||
// summary: Updates a namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to update
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/Namespace"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Namespace"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /namespaces/{namespaceID} namespaces deleteNamespace
|
||||
// ---
|
||||
// summary: Deletes a namespace with all lists
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to delete
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "404":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /namespaces/{namespaceID} namespaces getNamespace
|
||||
// ---
|
||||
// summary: gets one namespace with all todo tasks
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to show
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Namespace"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /namespaces/{namespaceID}/lists lists getNamespaceLists
|
||||
// ---
|
||||
// summary: gets all lists in that namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to show
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/List"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /namespaces namespaces getNamespaces
|
||||
// ---
|
||||
// summary: Get all namespaces the currently logged in user has at least read access
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Namespace"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /teams teams getTeams
|
||||
// ---
|
||||
// summary: gets all teams the current user is part of
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Team"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /teams/{teamID} teams getTeamByID
|
||||
// ---
|
||||
// summary: gets infos about the team
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Team"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /teams teams createTeam
|
||||
// ---
|
||||
// summary: Creates a team
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/Team"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Team"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /teams/{teamID} teams updateTeam
|
||||
// ---
|
||||
// summary: Updates a team
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team you want to update
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/Team"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Team"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /teams/{teamID} teams deleteTeam
|
||||
// ---
|
||||
// summary: Deletes a team
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team you want to delete
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /teams/{teamID}/members teams addTeamMember
|
||||
// ---
|
||||
// summary: Adds a member to a team
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team you want to add a member to
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/TeamMember"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/TeamMember"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /teams/{teamID}/members/{userID} teams removeTeamMember
|
||||
// ---
|
||||
// summary: Removes a member from a team
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team you want to delete a member
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: userID
|
||||
// in: path
|
||||
// description: ID of the user you want to remove from the team
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /namespaces/{namespaceID}/teams sharing getNamespaceTeams
|
||||
// ---
|
||||
// summary: gets all teams which have access to that namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to show
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Team"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /lists/{listID}/teams sharing getTeamsByList
|
||||
// ---
|
||||
// summary: gets all teams which have access to the list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to show
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Team"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /lists/{listID}/teams sharing addTeamToList
|
||||
// ---
|
||||
// summary: adds a team to a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/TeamList"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/TeamList"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /lists/{listID}/teams/{teamID} sharing deleteTeamFromList
|
||||
// ---
|
||||
// summary: removes a team from a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team to remove
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /namespaces/{namespaceID}/teams sharing giveTeamAccessToNamespace
|
||||
// ---
|
||||
// summary: Gives a team access to a namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/TeamNamespace"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/TeamNamespace"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /namespaces/{namespaceID}/teams/{teamID} sharing removeTeamFromNamespace
|
||||
// ---
|
||||
// summary: Removes a team from a namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team you want to remove
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /lists/{listID}/users sharing getUsersByList
|
||||
// ---
|
||||
// summary: gets all users which have access to the list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to show
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/User"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /lists/{listID}/users sharing addUserToList
|
||||
// ---
|
||||
// summary: adds a user to a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UserList"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/UserList"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /lists/{listID}/users/{userID} sharing deleteUserFromList
|
||||
// ---
|
||||
// summary: removes a user from a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: userID
|
||||
// in: path
|
||||
// description: ID of the user to remove
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation GET /namespaces/{namespaceID}/users sharing getNamespaceUsers
|
||||
// ---
|
||||
// summary: gets all users which have access to that namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to show
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/User"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation PUT /namespaces/{namespaceID}/users sharing giveUserAccessToNamespace
|
||||
// ---
|
||||
// summary: Gives a user access to a namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UserNamespace"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/UserNamespace"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation DELETE /namespaces/{namespaceID}/users/{userID} sharing removeUserFromNamespace
|
||||
// ---
|
||||
// summary: Removes a user from a namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: userID
|
||||
// in: path
|
||||
// description: ID of the user you want to remove
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /namespaces/{namespaceID}/users/{userID} sharing updateUserAccessToNamespace
|
||||
// ---
|
||||
// summary: Updates a users access to a namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: userID
|
||||
// in: path
|
||||
// description: ID of the user to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/NamespaceUser"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/NamespaceUser"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /namespaces/{namespaceID}/teams/{teamID} sharing updateTeamAccessToNamespace
|
||||
// ---
|
||||
// summary: Updates a teams access to a namespace
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: namespaceID
|
||||
// in: path
|
||||
// description: ID of the namespace to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/TeamNamespace"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/TeamNamespace"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /lists/{listID}/users/{userID} sharing updateUserAccessToList
|
||||
// ---
|
||||
// summary: Updates a users access to a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: userID
|
||||
// in: path
|
||||
// description: ID of the user to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UserList"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/UserList"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// swagger:operation POST /lists/{listID}/teams/{teamID} sharing updateTeamAccessToList
|
||||
// ---
|
||||
// summary: Updates a teams access to a list
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: listID
|
||||
// in: path
|
||||
// description: ID of the list to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: ID of the team to use
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/TeamList"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/TeamList"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "403":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
18
pkg/routes/api/v1/token_check.go
Normal file
18
pkg/routes/api/v1/token_check.go
Normal file
@ -0,0 +1,18 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
// CheckToken checks prints a message if the token is valid or not. Currently only used for testing pourposes.
|
||||
func CheckToken(c echo.Context) error {
|
||||
|
||||
user := c.Get("user").(*jwt.Token)
|
||||
|
||||
fmt.Println(user.Valid)
|
||||
|
||||
return c.JSON(418, models.Message{"🍵"})
|
||||
}
|
85
pkg/routes/api/v1/user_add_update.go
Normal file
85
pkg/routes/api/v1/user_add_update.go
Normal file
@ -0,0 +1,85 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// RegisterUser ...
|
||||
func RegisterUser(c echo.Context) error {
|
||||
|
||||
// swagger:operation POST /register user register
|
||||
// ---
|
||||
// summary: Creates a new user account
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/APIUserPassword"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/User"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
return userAddOrUpdate(c)
|
||||
}
|
||||
|
||||
// userAddOrUpdate is the handler to add a user
|
||||
func userAddOrUpdate(c echo.Context) error {
|
||||
|
||||
// TODO: prevent everyone from updating users
|
||||
|
||||
// Check for Request Content
|
||||
var datUser *models.APIUserPassword
|
||||
|
||||
if err := c.Bind(&datUser); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.Message{"No user model provided."})
|
||||
}
|
||||
|
||||
// Check if we have an ID other than the one in the struct
|
||||
id := c.Param("id")
|
||||
if id != "" {
|
||||
// Make int
|
||||
userID, err := strconv.ParseInt(id, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.Message{"Invalid ID."})
|
||||
}
|
||||
datUser.ID = userID
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
var exists = true
|
||||
_, err := models.GetUserByID(datUser.ID)
|
||||
if err != nil {
|
||||
if models.IsErrUserDoesNotExist(err) {
|
||||
exists = false
|
||||
} else {
|
||||
return c.JSON(http.StatusInternalServerError, models.Message{"Could not check if the user exists."})
|
||||
}
|
||||
}
|
||||
|
||||
// Insert or update the user
|
||||
var newUser models.User
|
||||
if exists {
|
||||
newUser, err = models.UpdateUser(datUser.APIFormat())
|
||||
} else {
|
||||
newUser, err = models.CreateUser(datUser.APIFormat())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, newUser)
|
||||
}
|
46
pkg/routes/api/v1/user_confirm_email.go
Normal file
46
pkg/routes/api/v1/user_confirm_email.go
Normal file
@ -0,0 +1,46 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UserConfirmEmail is the handler to confirm a user email
|
||||
func UserConfirmEmail(c echo.Context) error {
|
||||
// swagger:operation POST /user/confirm user confirmEmail
|
||||
// ---
|
||||
// summary: Confirms a users email address
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/EmailConfirm"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "404":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// Check for Request Content
|
||||
var emailConfirm models.EmailConfirm
|
||||
if err := c.Bind(&emailConfirm); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No token provided.")
|
||||
}
|
||||
|
||||
err := models.UserEmailConfirm(&emailConfirm)
|
||||
if err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{"The email was confirmed successfully."})
|
||||
}
|
49
pkg/routes/api/v1/user_delete.go
Normal file
49
pkg/routes/api/v1/user_delete.go
Normal file
@ -0,0 +1,49 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// UserDelete is the handler to delete a user
|
||||
func UserDelete(c echo.Context) error {
|
||||
|
||||
// TODO: only allow users to allow itself
|
||||
|
||||
id := c.Param("id")
|
||||
|
||||
// Make int
|
||||
userID, err := strconv.ParseInt(id, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.Message{"User ID is invalid."})
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
_, err = models.GetUserByID(userID)
|
||||
|
||||
if err != nil {
|
||||
if models.IsErrUserDoesNotExist(err) {
|
||||
return c.JSON(http.StatusNotFound, models.Message{"The user does not exist."})
|
||||
}
|
||||
return c.JSON(http.StatusInternalServerError, models.Message{"Could not get user."})
|
||||
}
|
||||
|
||||
// Get the doer options
|
||||
doer, err := models.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete it
|
||||
err = models.DeleteUserByID(userID, &doer)
|
||||
|
||||
if err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{"success"})
|
||||
}
|
44
pkg/routes/api/v1/user_list.go
Normal file
44
pkg/routes/api/v1/user_list.go
Normal file
@ -0,0 +1,44 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UserList gets all information about a user
|
||||
func UserList(c echo.Context) error {
|
||||
|
||||
// swagger:operation GET /users user list
|
||||
// ---
|
||||
// summary: Lists all users
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: s
|
||||
// description: A searchterm to search for a user by its username
|
||||
// in: query
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/User"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
s := c.QueryParam("s")
|
||||
users, err := models.ListUsers(s)
|
||||
if err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
// Obfuscate the mailadresses
|
||||
for in := range users {
|
||||
users[in].Email = ""
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, users)
|
||||
}
|
84
pkg/routes/api/v1/user_password_reset.go
Normal file
84
pkg/routes/api/v1/user_password_reset.go
Normal file
@ -0,0 +1,84 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UserResetPassword is the handler to change a users password
|
||||
func UserResetPassword(c echo.Context) error {
|
||||
// swagger:operation POST /user/password/reset user updatePassword
|
||||
// ---
|
||||
// summary: Resets a users password
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/PasswordReset"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "404":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// Check for Request Content
|
||||
var pwReset models.PasswordReset
|
||||
if err := c.Bind(&pwReset); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No password provided.")
|
||||
}
|
||||
|
||||
err := models.UserPasswordReset(&pwReset)
|
||||
if err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})
|
||||
}
|
||||
|
||||
// UserRequestResetPasswordToken is the handler to change a users password
|
||||
func UserRequestResetPasswordToken(c echo.Context) error {
|
||||
// swagger:operation POST /user/password/token user requestUpdatePasswordToken
|
||||
// ---
|
||||
// summary: Requests a token to reset a users password
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/PasswordTokenRequest"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "404":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// Check for Request Content
|
||||
var pwTokenReset models.PasswordTokenRequest
|
||||
if err := c.Bind(&pwTokenReset); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No user ID provided.")
|
||||
}
|
||||
|
||||
err := models.RequestUserPasswordResetToken(&pwTokenReset)
|
||||
if err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{"Token was sent."})
|
||||
}
|
38
pkg/routes/api/v1/user_show.go
Normal file
38
pkg/routes/api/v1/user_show.go
Normal file
@ -0,0 +1,38 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UserShow gets all informations about the current user
|
||||
func UserShow(c echo.Context) error {
|
||||
// swagger:operation GET /user user showUser
|
||||
// ---
|
||||
// summary: Shows the current user
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/User"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
userInfos, err := models.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.")
|
||||
}
|
||||
|
||||
user, err := models.GetUserByID(userInfos.ID)
|
||||
if err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, user)
|
||||
}
|
63
pkg/routes/api/v1/user_update_password.go
Normal file
63
pkg/routes/api/v1/user_update_password.go
Normal file
@ -0,0 +1,63 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UserPassword holds a user password. Used to update it.
|
||||
type UserPassword struct {
|
||||
OldPassword string `json:"old_password"`
|
||||
NewPassword string `json:"new_password"`
|
||||
}
|
||||
|
||||
// UserChangePassword is the handler to change a users password
|
||||
func UserChangePassword(c echo.Context) error {
|
||||
// swagger:operation POST /user/password user updatePassword
|
||||
// ---
|
||||
// summary: Shows the current user
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/Password"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "400":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "404":
|
||||
// "$ref": "#/responses/Message"
|
||||
// "500":
|
||||
// "$ref": "#/responses/Message"
|
||||
|
||||
// Check if the user is itself
|
||||
doer, err := models.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.")
|
||||
}
|
||||
|
||||
// Check for Request Content
|
||||
var newPW UserPassword
|
||||
if err := c.Bind(&newPW); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No password provided.")
|
||||
}
|
||||
|
||||
// Check the current password
|
||||
if _, err = models.CheckUserCredentials(&models.UserLogin{Username: doer.Username, Password: newPW.OldPassword}); err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
// Update the password
|
||||
if err = models.UpdateUserPassword(&doer, newPW.NewPassword); err != nil {
|
||||
return crud.HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})
|
||||
}
|
39
pkg/routes/crud/create.go
Normal file
39
pkg/routes/crud/create.go
Normal file
@ -0,0 +1,39 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CreateWeb is the handler to create an object
|
||||
func (c *WebHandler) CreateWeb(ctx echo.Context) error {
|
||||
// Get our model
|
||||
currentStruct := c.EmptyStruct()
|
||||
|
||||
// Get the object & bind params to struct
|
||||
if err := ParamBinder(currentStruct, ctx); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
|
||||
}
|
||||
|
||||
// Get the user to pass for later checks
|
||||
currentUser, err := models.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||
}
|
||||
|
||||
// Check rights
|
||||
if !currentStruct.CanCreate(¤tUser) {
|
||||
log.Log.Noticef("%s [ID: %d] tried to create while not having the rights for it", currentUser.Username, currentUser.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
// Create
|
||||
err = currentStruct.Create(¤tUser)
|
||||
if err != nil {
|
||||
return HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusCreated, currentStruct)
|
||||
}
|
37
pkg/routes/crud/delete.go
Normal file
37
pkg/routes/crud/delete.go
Normal file
@ -0,0 +1,37 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// DeleteWeb is the web handler to delete something
|
||||
func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
|
||||
|
||||
// Get our model
|
||||
currentStruct := c.EmptyStruct()
|
||||
|
||||
// Bind params to struct
|
||||
if err := ParamBinder(currentStruct, ctx); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid URL param.")
|
||||
}
|
||||
|
||||
// Check if the user has the right to delete
|
||||
currentUser, err := models.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
if !currentStruct.CanDelete(¤tUser) {
|
||||
log.Log.Noticef("%s [ID: %d] tried to delete while not having the rights for it", currentUser.Username, currentUser.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
err = currentStruct.Delete()
|
||||
if err != nil {
|
||||
return HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, models.Message{"Successfully deleted."})
|
||||
}
|
30
pkg/routes/crud/helper.go
Normal file
30
pkg/routes/crud/helper.go
Normal file
@ -0,0 +1,30 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// WebHandler defines the webhandler object
|
||||
// This does web stuff, aka returns json etc. Uses CRUDable Methods to get the data
|
||||
type WebHandler struct {
|
||||
EmptyStruct func() CObject
|
||||
}
|
||||
|
||||
// CObject is the definition of our object, holds the structs
|
||||
type CObject interface {
|
||||
models.CRUDable
|
||||
models.Rights
|
||||
}
|
||||
|
||||
// HandleHTTPError does what it says
|
||||
func HandleHTTPError(err error) *echo.HTTPError {
|
||||
if a, has := err.(models.HTTPErrorProcessor); has {
|
||||
errDetails := a.HTTPError()
|
||||
return echo.NewHTTPError(errDetails.HTTPCode, errDetails)
|
||||
}
|
||||
log.Log.Error(err.Error())
|
||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||
}
|
272
pkg/routes/crud/paramBinder.go
Normal file
272
pkg/routes/crud/paramBinder.go
Normal file
@ -0,0 +1,272 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/labstack/echo"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const paramTagName = "param"
|
||||
|
||||
// ParamBinder binds parameters to a struct.
|
||||
// Currently a working implementation, waiting to implement this officially into echo.
|
||||
func ParamBinder(i interface{}, c echo.Context) (err error) {
|
||||
|
||||
// Default binder
|
||||
db := new(echo.DefaultBinder)
|
||||
if err = db.Bind(i, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
paramNames := c.ParamNames()
|
||||
paramValues := c.ParamValues()
|
||||
paramVars := make(map[string][]string)
|
||||
for in, name := range paramNames {
|
||||
// Hotfix for an echo bug where a param name would show up which dont exist
|
||||
names := strings.Split(name, ",")
|
||||
for _, n := range names {
|
||||
paramVars[n] = append(paramVars[name], paramValues[in])
|
||||
}
|
||||
}
|
||||
|
||||
b := Binder{}
|
||||
err = b.bindData(i, paramVars, paramTagName)
|
||||
|
||||
/*
|
||||
// Our custom magic starts here
|
||||
paramNames := c.ParamNames()
|
||||
paramValues := c.ParamValues()
|
||||
|
||||
v := reflect.ValueOf(i)
|
||||
t := reflect.TypeOf(i)
|
||||
s := reflect.ValueOf(i).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
f := s.Field(i)
|
||||
|
||||
// Check if it has a param tag
|
||||
tag := field.Tag.Get(paramTagName)
|
||||
if tag != "" {
|
||||
// If it has one, range over all url parameters to see if we have a match
|
||||
for in, name := range paramNames {
|
||||
// Found match
|
||||
if tag == name {
|
||||
// Put the value of that match in our sruct
|
||||
switch field.Type.Name() {
|
||||
case "int64": // SetInt only accepts int64, so the struct field can only have int64 of int (no int32/16/int...)
|
||||
intParam, err := strconv.ParseInt(paramValues[in], 10, 64)
|
||||
f.SetInt(intParam)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "string":
|
||||
f.SetString(paramValues[in])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//f.SetString("blub")
|
||||
|
||||
}*/
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Binder represents a binder
|
||||
type Binder struct{}
|
||||
|
||||
func (b *Binder) bindData(ptr interface{}, data map[string][]string, tag string) error {
|
||||
typ := reflect.TypeOf(ptr).Elem()
|
||||
val := reflect.ValueOf(ptr).Elem()
|
||||
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return errors.New("Binding element must be a struct")
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
typeField := typ.Field(i)
|
||||
structField := val.Field(i)
|
||||
if !structField.CanSet() {
|
||||
continue
|
||||
}
|
||||
structFieldKind := structField.Kind()
|
||||
inputFieldName := typeField.Tag.Get(tag)
|
||||
|
||||
if inputFieldName == "" {
|
||||
inputFieldName = typeField.Name
|
||||
// If tag is nil, we inspect if the field is a struct.
|
||||
if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
|
||||
err := b.bindData(structField.Addr().Interface(), data, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
inputValue, exists := data[inputFieldName]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
// Call this first, in case we're dealing with an alias to an array type
|
||||
if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
numElems := len(inputValue)
|
||||
if structFieldKind == reflect.Slice && numElems > 0 {
|
||||
sliceOf := structField.Type().Elem().Kind()
|
||||
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
||||
for j := 0; j < numElems; j++ {
|
||||
if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
val.Field(i).Set(slice)
|
||||
} else {
|
||||
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
||||
// But also call it here, in case we're dealing with an array of BindUnmarshalers
|
||||
if ok, err := unmarshalField(valueKind, val, structField); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
switch valueKind {
|
||||
case reflect.Int:
|
||||
return setIntField(val, 0, structField)
|
||||
case reflect.Int8:
|
||||
return setIntField(val, 8, structField)
|
||||
case reflect.Int16:
|
||||
return setIntField(val, 16, structField)
|
||||
case reflect.Int32:
|
||||
return setIntField(val, 32, structField)
|
||||
case reflect.Int64:
|
||||
return setIntField(val, 64, structField)
|
||||
case reflect.Uint:
|
||||
return setUintField(val, 0, structField)
|
||||
case reflect.Uint8:
|
||||
return setUintField(val, 8, structField)
|
||||
case reflect.Uint16:
|
||||
return setUintField(val, 16, structField)
|
||||
case reflect.Uint32:
|
||||
return setUintField(val, 32, structField)
|
||||
case reflect.Uint64:
|
||||
return setUintField(val, 64, structField)
|
||||
case reflect.Bool:
|
||||
return setBoolField(val, structField)
|
||||
case reflect.Float32:
|
||||
return setFloatField(val, 32, structField)
|
||||
case reflect.Float64:
|
||||
return setFloatField(val, 64, structField)
|
||||
case reflect.String:
|
||||
structField.SetString(val)
|
||||
default:
|
||||
return errors.New("unknown type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setIntField(value string, bitSize int, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "0"
|
||||
}
|
||||
intVal, err := strconv.ParseInt(value, 10, bitSize)
|
||||
if err == nil {
|
||||
field.SetInt(intVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setUintField(value string, bitSize int, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "0"
|
||||
}
|
||||
uintVal, err := strconv.ParseUint(value, 10, bitSize)
|
||||
if err == nil {
|
||||
field.SetUint(uintVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setBoolField(value string, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "false"
|
||||
}
|
||||
boolVal, err := strconv.ParseBool(value)
|
||||
if err == nil {
|
||||
field.SetBool(boolVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setFloatField(value string, bitSize int, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "0.0"
|
||||
}
|
||||
floatVal, err := strconv.ParseFloat(value, bitSize)
|
||||
if err == nil {
|
||||
field.SetFloat(floatVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// BindUnmarshaler type
|
||||
type BindUnmarshaler interface {
|
||||
// UnmarshalParam decodes and assigns a value from an form or query param.
|
||||
UnmarshalParam(param string) error
|
||||
}
|
||||
|
||||
// bindUnmarshaler attempts to unmarshal a reflect.Value into a BindUnmarshaler
|
||||
func bindUnmarshaler(field reflect.Value) (BindUnmarshaler, bool) {
|
||||
ptr := reflect.New(field.Type())
|
||||
if ptr.CanInterface() {
|
||||
iface := ptr.Interface()
|
||||
if unmarshaler, ok := iface.(BindUnmarshaler); ok {
|
||||
return unmarshaler, ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
|
||||
switch valueKind {
|
||||
case reflect.Ptr:
|
||||
return unmarshalFieldPtr(val, field)
|
||||
default:
|
||||
return unmarshalFieldNonPtr(val, field)
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
|
||||
if unmarshaler, ok := bindUnmarshaler(field); ok {
|
||||
err := unmarshaler.UnmarshalParam(value)
|
||||
field.Set(reflect.ValueOf(unmarshaler).Elem())
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
|
||||
if field.IsNil() {
|
||||
// Initialize the pointer to a nil value
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
}
|
||||
return unmarshalFieldNonPtr(value, field.Elem())
|
||||
}
|
30
pkg/routes/crud/read_all.go
Normal file
30
pkg/routes/crud/read_all.go
Normal file
@ -0,0 +1,30 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ReadAllWeb is the webhandler to get all objects of a type
|
||||
func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
|
||||
// Get our model
|
||||
currentStruct := c.EmptyStruct()
|
||||
|
||||
currentUser, err := models.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||
}
|
||||
|
||||
// Get the object & bind params to struct
|
||||
if err := ParamBinder(currentStruct, ctx); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
|
||||
}
|
||||
|
||||
lists, err := currentStruct.ReadAll(¤tUser)
|
||||
if err != nil {
|
||||
return HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, lists)
|
||||
}
|
38
pkg/routes/crud/read_one.go
Normal file
38
pkg/routes/crud/read_one.go
Normal file
@ -0,0 +1,38 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ReadOneWeb is the webhandler to get one object
|
||||
func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
|
||||
// Get our model
|
||||
currentStruct := c.EmptyStruct()
|
||||
|
||||
// Get the object & bind params to struct
|
||||
if err := ParamBinder(currentStruct, ctx); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
|
||||
}
|
||||
|
||||
// Get our object
|
||||
err := currentStruct.ReadOne()
|
||||
if err != nil {
|
||||
return HandleHTTPError(err)
|
||||
}
|
||||
|
||||
// Check rights
|
||||
// We can only check the rights on a full object, which is why we need to check it afterwards
|
||||
currentUser, err := models.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||
}
|
||||
if !currentStruct.CanRead(¤tUser) {
|
||||
log.Log.Noticef("%s [ID: %d] tried to read while not having the rights for it", currentUser.Username, currentUser.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this")
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, currentStruct)
|
||||
}
|
38
pkg/routes/crud/update.go
Normal file
38
pkg/routes/crud/update.go
Normal file
@ -0,0 +1,38 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UpdateWeb is the webhandler to update an object
|
||||
func (c *WebHandler) UpdateWeb(ctx echo.Context) error {
|
||||
|
||||
// Get our model
|
||||
currentStruct := c.EmptyStruct()
|
||||
|
||||
// Get the object & bind params to struct
|
||||
if err := ParamBinder(currentStruct, ctx); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided.")
|
||||
}
|
||||
|
||||
// Check if the user has the right to do that
|
||||
currentUser, err := models.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||
}
|
||||
if !currentStruct.CanUpdate(¤tUser) {
|
||||
log.Log.Noticef("%s [ID: %d] tried to update while not having the rights for it", currentUser.Username, currentUser.ID)
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
// Do the update
|
||||
err = currentStruct.Update()
|
||||
if err != nil {
|
||||
return HandleHTTPError(err)
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, currentStruct)
|
||||
}
|
172
pkg/routes/routes.go
Normal file
172
pkg/routes/routes.go
Normal file
@ -0,0 +1,172 @@
|
||||
// Package v1 List API.
|
||||
//
|
||||
// This documentation describes the List API.
|
||||
//
|
||||
// Schemes: http, https
|
||||
// BasePath: /api/v1
|
||||
// Version: 0.1
|
||||
// License: GPLv3
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// Security:
|
||||
// - AuthorizationHeaderToken :
|
||||
//
|
||||
// SecurityDefinitions:
|
||||
// AuthorizationHeaderToken:
|
||||
// type: apiKey
|
||||
// name: Authorization
|
||||
// in: header
|
||||
//
|
||||
// swagger:meta
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
_ "code.vikunja.io/api/pkg/routes/api/v1/swagger" // for docs generation
|
||||
"code.vikunja.io/api/pkg/routes/crud"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// NewEcho registers a new Echo instance
|
||||
func NewEcho() *echo.Echo {
|
||||
e := echo.New()
|
||||
|
||||
// Logger
|
||||
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
||||
Format: "${time_rfc3339_nano}: ${remote_ip} ${method} ${status} ${uri} ${latency_human} - ${user_agent}\n",
|
||||
}))
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all routes for the application
|
||||
func RegisterRoutes(e *echo.Echo) {
|
||||
|
||||
// CORS_SHIT
|
||||
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||
AllowOrigins: []string{"*"},
|
||||
}))
|
||||
|
||||
// API Routes
|
||||
a := e.Group("/api/v1")
|
||||
|
||||
// Swagger UI
|
||||
a.Static("/swagger", "public/swagger")
|
||||
|
||||
a.POST("/login", apiv1.Login)
|
||||
a.POST("/register", apiv1.RegisterUser)
|
||||
a.POST("/user/password/token", apiv1.UserRequestResetPasswordToken)
|
||||
a.POST("/user/password/reset", apiv1.UserResetPassword)
|
||||
a.POST("/user/confirm", apiv1.UserConfirmEmail)
|
||||
|
||||
// ===== Routes with Authetification =====
|
||||
// Authetification
|
||||
a.Use(middleware.JWT([]byte(viper.GetString("service.JWTSecret"))))
|
||||
a.POST("/tokenTest", apiv1.CheckToken)
|
||||
|
||||
// User stuff
|
||||
a.GET("/user", apiv1.UserShow)
|
||||
a.POST("/user/password", apiv1.UserChangePassword)
|
||||
a.GET("/users", apiv1.UserList)
|
||||
|
||||
listHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.List{}
|
||||
},
|
||||
}
|
||||
a.GET("/lists", listHandler.ReadAllWeb)
|
||||
a.GET("/lists/:list", listHandler.ReadOneWeb)
|
||||
a.POST("/lists/:list", listHandler.UpdateWeb)
|
||||
a.DELETE("/lists/:list", listHandler.DeleteWeb)
|
||||
a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb)
|
||||
|
||||
taskHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.ListTask{}
|
||||
},
|
||||
}
|
||||
a.PUT("/lists/:list", taskHandler.CreateWeb)
|
||||
a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb)
|
||||
a.POST("/tasks/:listtask", taskHandler.UpdateWeb)
|
||||
|
||||
listTeamHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.TeamList{}
|
||||
},
|
||||
}
|
||||
a.GET("/lists/:list/teams", listTeamHandler.ReadAllWeb)
|
||||
a.PUT("/lists/:list/teams", listTeamHandler.CreateWeb)
|
||||
a.DELETE("/lists/:list/teams/:team", listTeamHandler.DeleteWeb)
|
||||
a.POST("/lists/:list/teams/:team", listTeamHandler.UpdateWeb)
|
||||
|
||||
listUserHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.ListUser{}
|
||||
},
|
||||
}
|
||||
a.GET("/lists/:list/users", listUserHandler.ReadAllWeb)
|
||||
a.PUT("/lists/:list/users", listUserHandler.CreateWeb)
|
||||
a.DELETE("/lists/:list/users/:user", listUserHandler.DeleteWeb)
|
||||
a.POST("/lists/:list/users/:user", listUserHandler.UpdateWeb)
|
||||
|
||||
namespaceHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.Namespace{}
|
||||
},
|
||||
}
|
||||
a.GET("/namespaces", namespaceHandler.ReadAllWeb)
|
||||
a.PUT("/namespaces", namespaceHandler.CreateWeb)
|
||||
a.GET("/namespaces/:namespace", namespaceHandler.ReadOneWeb)
|
||||
a.POST("/namespaces/:namespace", namespaceHandler.UpdateWeb)
|
||||
a.DELETE("/namespaces/:namespace", namespaceHandler.DeleteWeb)
|
||||
a.GET("/namespaces/:namespace/lists", apiv1.GetListsByNamespaceID)
|
||||
|
||||
namespaceTeamHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.TeamNamespace{}
|
||||
},
|
||||
}
|
||||
a.GET("/namespaces/:namespace/teams", namespaceTeamHandler.ReadAllWeb)
|
||||
a.PUT("/namespaces/:namespace/teams", namespaceTeamHandler.CreateWeb)
|
||||
a.DELETE("/namespaces/:namespace/teams/:team", namespaceTeamHandler.DeleteWeb)
|
||||
a.POST("/namespaces/:namespace/teams/:team", namespaceTeamHandler.UpdateWeb)
|
||||
|
||||
namespaceUserHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.NamespaceUser{}
|
||||
},
|
||||
}
|
||||
a.GET("/namespaces/:namespace/users", namespaceUserHandler.ReadAllWeb)
|
||||
a.PUT("/namespaces/:namespace/users", namespaceUserHandler.CreateWeb)
|
||||
a.DELETE("/namespaces/:namespace/users/:user", namespaceUserHandler.DeleteWeb)
|
||||
a.POST("/namespaces/:namespace/users/:user", namespaceUserHandler.UpdateWeb)
|
||||
|
||||
teamHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.Team{}
|
||||
},
|
||||
}
|
||||
a.GET("/teams", teamHandler.ReadAllWeb)
|
||||
a.GET("/teams/:team", teamHandler.ReadOneWeb)
|
||||
a.PUT("/teams", teamHandler.CreateWeb)
|
||||
a.POST("/teams/:team", teamHandler.UpdateWeb)
|
||||
a.DELETE("/teams/:team", teamHandler.DeleteWeb)
|
||||
|
||||
teamMemberHandler := &crud.WebHandler{
|
||||
EmptyStruct: func() crud.CObject {
|
||||
return &models.TeamMember{}
|
||||
},
|
||||
}
|
||||
a.PUT("/teams/:team/members", teamMemberHandler.CreateWeb)
|
||||
a.DELETE("/teams/:team/members/:user", teamMemberHandler.DeleteWeb)
|
||||
}
|
Reference in New Issue
Block a user