vikunja-api/pkg/routes/routes.go

691 lines
23 KiB
Go
Raw Normal View History

2020-02-07 17:27:45 +01:00
// Vikunja is a to-do list application to facilitate your life.
2021-02-02 20:19:13 +01:00
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
2018-11-26 21:17:33 +01:00
//
// This program is free software: you can redistribute it and/or modify
2020-12-23 16:41:52 +01:00
// it under the terms of the GNU Affero General Public Licensee as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2018-11-26 21:17:33 +01:00
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2020-12-23 16:41:52 +01:00
// GNU Affero General Public Licensee for more details.
2018-11-26 21:17:33 +01:00
//
2020-12-23 16:41:52 +01:00
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2018-11-26 21:17:33 +01:00
// @title Vikunja API
2020-02-07 17:27:45 +01:00
// @description This is the documentation for the [Vikunja](http://vikunja.io) API. Vikunja is a cross-plattform To-do-application with a lot of features, such as sharing lists with users or teams. <!-- ReDoc-Inject: <security-definitions> -->
2019-10-23 23:11:40 +02:00
// @description # Pagination
// @description Every endpoint capable of pagination will return two headers:
// @description * `x-pagination-total-pages`: The total number of available pages for this request
// @description * `x-pagination-result-count`: The number of items returned for this request.
// @description # Rights
// @description All endpoints which return a single item (list, task, namespace, etc.) - no array - will also return a `x-max-right` header with the max right the user has on this item as an int where `0` is `Read Only`, `1` is `Read & Write` and `2` is `Admin`.
// @description This can be used to show or hide ui elements based on the rights the user has.
2019-01-03 23:22:06 +01:00
// @description # Authorization
2019-10-16 22:52:29 +02:00
// @description **JWT-Auth:** Main authorization method, used for most of the requests. Needs `Authorization: Bearer <jwt-token>`-header to authenticate successfully.
2019-01-03 23:22:06 +01:00
// @description
// @description **BasicAuth:** Only used when requesting tasks via caldav.
// @description <!-- ReDoc-Inject: <security-definitions> -->
// @BasePath /api/v1
2019-01-03 23:22:06 +01:00
// @license.url http://code.vikunja.io/api/src/branch/master/LICENSE
// @license.name GPLv3
// @contact.url http://vikunja.io/en/contact/
// @contact.name General Vikunja contact
// @contact.email hello@vikunja.io
// @securityDefinitions.basic BasicAuth
2019-01-03 23:22:06 +01:00
// @securityDefinitions.apikey JWTKeyAuth
// @in header
// @name Authorization
2018-06-10 11:11:41 +02:00
2018-08-29 15:22:17 +02:00
package routes
2018-06-10 11:11:41 +02:00
import (
"code.vikunja.io/api/pkg/modules/migration/ticktick"
"errors"
"fmt"
"net/url"
"strings"
"time"
2022-10-09 18:56:29 +02:00
"code.vikunja.io/api/pkg/modules/migration/ticktick"
"github.com/ulule/limiter/v3"
vikunja_file "code.vikunja.io/api/pkg/modules/migration/vikunja-file"
"code.vikunja.io/api/pkg/config"
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
"code.vikunja.io/api/pkg/db"
2018-12-01 00:26:56 +01:00
"code.vikunja.io/api/pkg/log"
2018-11-17 00:17:37 +01:00
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/auth"
"code.vikunja.io/api/pkg/modules/auth/openid"
"code.vikunja.io/api/pkg/modules/background"
backgroundHandler "code.vikunja.io/api/pkg/modules/background/handler"
"code.vikunja.io/api/pkg/modules/background/unsplash"
"code.vikunja.io/api/pkg/modules/background/upload"
"code.vikunja.io/api/pkg/modules/migration"
migrationHandler "code.vikunja.io/api/pkg/modules/migration/handler"
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
microsofttodo "code.vikunja.io/api/pkg/modules/migration/microsoft-todo"
"code.vikunja.io/api/pkg/modules/migration/todoist"
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
"code.vikunja.io/api/pkg/modules/migration/trello"
"code.vikunja.io/api/pkg/modules/migration/wunderlist"
2018-10-31 13:42:38 +01:00
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
2019-05-22 19:48:48 +02:00
"code.vikunja.io/api/pkg/routes/caldav"
2019-02-17 20:53:04 +01:00
_ "code.vikunja.io/api/pkg/swagger" // To generate swagger docs
"code.vikunja.io/api/pkg/version"
2018-12-01 00:26:56 +01:00
"code.vikunja.io/web"
"code.vikunja.io/web/handler"
"github.com/getsentry/sentry-go"
sentryecho "github.com/getsentry/sentry-go/echo"
"github.com/golang-jwt/jwt/v4"
2019-05-07 21:42:24 +02:00
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
2019-01-25 12:40:54 +01:00
elog "github.com/labstack/gommon/log"
2018-06-10 11:11:41 +02:00
)
// NewEcho registers a new Echo instance
func NewEcho() *echo.Echo {
e := echo.New()
2018-11-03 16:05:45 +01:00
e.HideBanner = true
2019-01-25 12:40:54 +01:00
if l, ok := e.Logger.(*elog.Logger); ok {
if config.LogEcho.GetString() == "off" {
2019-01-25 12:40:54 +01:00
l.SetLevel(elog.OFF)
}
l.EnableColor()
l.SetHeader(log.ErrFmt)
l.SetOutput(log.GetLogWriter("echo"))
}
2018-06-10 11:11:41 +02:00
// Logger
if config.LogHTTP.GetString() != "off" {
2019-01-25 12:40:54 +01:00
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: log.WebFmt + "\n",
Output: log.GetLogWriter("http"),
}))
}
2018-06-10 11:11:41 +02:00
// panic recover
e.Use(middleware.Recover())
if config.ServiceSentryDsn.GetString() != "" {
if err := sentry.Init(sentry.ClientOptions{
Dsn: config.ServiceSentryDsn.GetString(),
AttachStacktrace: true,
Release: version.Version,
}); err != nil {
log.Criticalf("Sentry init failed: %s", err)
}
defer sentry.Flush(5 * time.Second)
e.Use(sentryecho.New(sentryecho.Options{
Repanic: true,
}))
e.HTTPErrorHandler = func(err error, c echo.Context) {
// Only capture errors not already handled by echo
2022-03-27 17:52:33 +02:00
var herr *echo.HTTPError
if errors.As(err, &herr) {
hub := sentryecho.GetHubFromContext(c)
if hub != nil {
hub.WithScope(func(scope *sentry.Scope) {
scope.SetExtra("url", c.Request().URL)
hub.CaptureException(err)
})
} else {
sentry.CaptureException(err)
log.Debugf("Could not add context for sending error '%s' to sentry", err.Error())
}
log.Debugf("Error '%s' sent to sentry", err.Error())
}
e.DefaultHTTPErrorHandler(err, c)
}
}
2018-11-17 00:17:37 +01:00
// Validation
e.Validator = &CustomValidator{}
2019-03-24 10:13:40 +01:00
// Handler config
handler.SetAuthProvider(&web.Auths{
AuthObject: auth.GetAuthFromClaims,
2019-03-24 10:13:40 +01:00
})
2019-07-20 20:12:10 +02:00
handler.SetLoggingProvider(log.GetLogger())
2019-10-23 23:11:40 +02:00
handler.SetMaxItemsPerPage(config.ServiceMaxItemsPerPage.GetInt())
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
handler.SetSessionFactory(db.NewSession)
2019-03-24 10:13:40 +01:00
2018-06-10 11:11:41 +02:00
return e
}
// RegisterRoutes registers all routes for the application
func RegisterRoutes(e *echo.Echo) {
if config.ServiceEnableCaldav.GetBool() {
2019-05-22 19:48:48 +02:00
// Caldav routes
wkg := e.Group("/.well-known")
wkg.Use(middleware.BasicAuth(caldav.BasicAuth))
2019-05-22 19:48:48 +02:00
wkg.Any("/caldav", caldav.PrincipalHandler)
wkg.Any("/caldav/", caldav.PrincipalHandler)
c := e.Group("/dav")
registerCalDavRoutes(c)
}
// healthcheck
e.GET("/health", HealthcheckHandler)
// static files
if static := config.ServiceStaticpath.GetString(); static != "" {
e.Use(middleware.Static(static))
}
2018-09-07 22:49:16 +02:00
// CORS_SHIT
if config.CorsEnable.GetBool() {
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: config.CorsOrigins.GetStringSlice(),
MaxAge: config.CorsMaxAge.GetInt(),
Skipper: func(context echo.Context) bool {
// Since it is not possible to register this middleware just for the api group,
// we just disable it when for caldav requests.
// Caldav requires OPTIONS requests to be answered in a specific manner,
// not doing this would break the caldav implementation
return strings.HasPrefix(context.Path(), "/dav")
},
}))
}
2018-06-10 11:11:41 +02:00
// API Routes
a := e.Group("/api/v1")
2019-05-22 19:48:48 +02:00
registerAPIRoutes(a)
}
func registerAPIRoutes(a *echo.Group) {
2018-06-10 11:11:41 +02:00
// This is the group with no auth
// It is its own group to be able to rate limit this based on different heuristics
n := a.Group("")
setupRateLimit(n, "ip")
// Echo does not unescape url path params by default. To make sure values bound as :param in urls are passed
// properly to handlers, we use this middleware to unescape them.
// See https://kolaente.dev/vikunja/api/issues/1224
// See https://github.com/labstack/echo/issues/766
a.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
params := make([]string, 0, len(c.ParamValues()))
for _, param := range c.ParamValues() {
p, err := url.PathUnescape(param)
if err != nil {
return err
}
params = append(params, p)
}
c.SetParamValues(params...)
return next(c)
}
})
2019-01-03 23:22:06 +01:00
// Docs
n.GET("/docs.json", apiv1.DocsJSON)
n.GET("/docs", apiv1.RedocUI)
2018-09-17 18:34:46 +02:00
// Prometheus endpoint
setupMetrics(n)
// Separate route for unauthenticated routes to enable rate limits for it
ur := a.Group("")
rate := limiter.Rate{
Period: 60 * time.Second,
Limit: 10,
}
rateLimiter := createRateLimiter(rate)
ur.Use(RateLimit(rateLimiter, "ip"))
if config.AuthLocalEnabled.GetBool() {
// User stuff
ur.POST("/login", apiv1.Login)
ur.POST("/register", apiv1.RegisterUser)
ur.POST("/user/password/token", apiv1.UserRequestResetPasswordToken)
ur.POST("/user/password/reset", apiv1.UserResetPassword)
ur.POST("/user/confirm", apiv1.UserConfirmEmail)
}
if config.AuthOpenIDEnabled.GetBool() {
ur.POST("/auth/openid/:provider/callback", openid.HandleCallback)
}
2018-06-10 11:11:41 +02:00
// Testing
if config.ServiceTestingtoken.GetString() != "" {
n.PATCH("/test/:table", apiv1.HandleTesting)
}
2019-07-16 00:54:38 +02:00
// Info endpoint
n.GET("/info", apiv1.Info)
2019-07-16 00:54:38 +02:00
// Avatar endpoint
2020-12-18 23:11:11 +01:00
n.GET("/avatar/:username", apiv1.GetAvatar)
// Link share auth
if config.ServiceEnableLinkSharing.GetBool() {
ur.POST("/shares/:share/auth", apiv1.AuthenticateLinkShare)
}
2019-07-21 23:27:30 +02:00
// ===== Routes with Authetication =====
2018-06-10 11:11:41 +02:00
// Authetification
a.Use(middleware.JWTWithConfig(middleware.JWTConfig{
// Custom parse function to make the middleware work with the github.com/golang-jwt/jwt/v4 package.
// See https://github.com/labstack/echo/pull/1916#issuecomment-878046299
ParseTokenFunc: func(auth string, c echo.Context) (interface{}, error) {
keyFunc := func(t *jwt.Token) (interface{}, error) {
if t.Method.Alg() != "HS256" {
return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"])
}
return []byte(config.ServiceJWTSecret.GetString()), nil
}
token, err := jwt.Parse(auth, keyFunc)
if err != nil {
return nil, err
}
if !token.Valid {
return nil, errors.New("invalid token")
}
return token, nil
},
}))
2018-12-01 00:26:56 +01:00
2019-07-21 23:27:30 +02:00
// Rate limit
setupRateLimit(a, config.RateLimitKind.GetString())
2019-07-21 23:27:30 +02:00
// Middleware to collect metrics
2019-07-21 23:44:12 +02:00
setupMetricsMiddleware(a)
2018-06-10 11:11:41 +02:00
a.POST("/tokenTest", apiv1.CheckToken)
2018-06-10 14:14:10 +02:00
2018-09-20 19:42:01 +02:00
// User stuff
u := a.Group("/user")
u.GET("", apiv1.UserShow)
u.POST("/password", apiv1.UserChangePassword)
u.GET("s", apiv1.UserList)
u.POST("/token", apiv1.RenewToken)
u.POST("/settings/email", apiv1.UpdateUserEmail)
u.GET("/settings/avatar", apiv1.GetUserAvatarProvider)
u.POST("/settings/avatar", apiv1.ChangeUserAvatarProvider)
u.PUT("/settings/avatar/upload", apiv1.UploadAvatar)
u.POST("/settings/general", apiv1.UpdateGeneralUserSettings)
u.POST("/export/request", apiv1.RequestUserDataExport)
u.POST("/export/download", apiv1.DownloadUserDataExport)
u.GET("/timezones", apiv1.GetAvailableTimezones)
u.PUT("/settings/token/caldav", apiv1.GenerateCaldavToken)
u.GET("/settings/token/caldav", apiv1.GetCaldavTokens)
u.DELETE("/settings/token/caldav/:id", apiv1.DeleteCaldavToken)
if config.ServiceEnableTotp.GetBool() {
u.GET("/settings/totp", apiv1.UserTOTP)
u.POST("/settings/totp/enroll", apiv1.UserTOTPEnroll)
u.POST("/settings/totp/enable", apiv1.UserTOTPEnable)
u.POST("/settings/totp/disable", apiv1.UserTOTPDisable)
u.GET("/settings/totp/qrcode", apiv1.UserTOTPQrCode)
}
2018-09-20 19:42:01 +02:00
// User deletion
if config.ServiceEnableUserDeletion.GetBool() {
u.POST("/deletion/request", apiv1.UserRequestDeletion)
u.POST("/deletion/confirm", apiv1.UserConfirmDeletion)
u.POST("/deletion/cancel", apiv1.UserCancelDeletion)
}
2018-12-01 00:26:56 +01:00
listHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.List{}
},
2018-07-07 14:19:34 +02:00
}
a.GET("/lists", listHandler.ReadAllWeb)
2018-07-21 15:08:46 +02:00
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)
a.GET("/lists/:list/listusers", apiv1.ListUsersForList)
if config.ServiceEnableLinkSharing.GetBool() {
listSharingHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.LinkSharing{}
},
}
a.PUT("/lists/:list/shares", listSharingHandler.CreateWeb)
a.GET("/lists/:list/shares", listSharingHandler.ReadAllWeb)
a.GET("/lists/:list/shares/:share", listSharingHandler.ReadOneWeb)
a.DELETE("/lists/:list/shares/:share", listSharingHandler.DeleteWeb)
}
2019-12-01 14:38:11 +01:00
taskCollectionHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TaskCollection{}
},
}
a.GET("/lists/:list/tasks", taskCollectionHandler.ReadAllWeb)
kanbanBucketHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.Bucket{}
},
}
a.GET("/lists/:list/buckets", kanbanBucketHandler.ReadAllWeb)
a.PUT("/lists/:list/buckets", kanbanBucketHandler.CreateWeb)
a.POST("/lists/:list/buckets/:bucket", kanbanBucketHandler.UpdateWeb)
a.DELETE("/lists/:list/buckets/:bucket", kanbanBucketHandler.DeleteWeb)
listDuplicateHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.ListDuplicate{}
},
}
a.PUT("/lists/:listid/duplicate", listDuplicateHandler.CreateWeb)
2018-12-01 00:26:56 +01:00
taskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
2019-08-14 22:19:04 +02:00
return &models.Task{}
},
}
2018-08-30 08:09:17 +02:00
a.PUT("/lists/:list", taskHandler.CreateWeb)
a.GET("/tasks/:listtask", taskHandler.ReadOneWeb)
2019-12-01 14:38:11 +01:00
a.GET("/tasks/all", taskCollectionHandler.ReadAllWeb)
2018-08-30 08:09:17 +02:00
a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb)
a.POST("/tasks/:listtask", taskHandler.UpdateWeb)
2018-12-28 22:49:46 +01:00
bulkTaskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.BulkTask{}
},
}
a.POST("/tasks/bulk", bulkTaskHandler.UpdateWeb)
2019-01-08 20:13:07 +01:00
assigneeTaskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
2019-08-14 22:19:04 +02:00
return &models.TaskAssginee{}
2019-01-08 20:13:07 +01:00
},
}
a.PUT("/tasks/:listtask/assignees", assigneeTaskHandler.CreateWeb)
a.DELETE("/tasks/:listtask/assignees/:user", assigneeTaskHandler.DeleteWeb)
a.GET("/tasks/:listtask/assignees", assigneeTaskHandler.ReadAllWeb)
bulkAssigneeHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.BulkAssignees{}
},
}
2019-01-10 00:08:12 +01:00
a.POST("/tasks/:listtask/assignees/bulk", bulkAssigneeHandler.CreateWeb)
2019-01-08 20:13:07 +01:00
2018-12-31 02:18:41 +01:00
labelTaskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.LabelTask{}
},
}
a.PUT("/tasks/:listtask/labels", labelTaskHandler.CreateWeb)
a.DELETE("/tasks/:listtask/labels/:label", labelTaskHandler.DeleteWeb)
a.GET("/tasks/:listtask/labels", labelTaskHandler.ReadAllWeb)
2019-01-10 00:08:12 +01:00
bulkLabelTaskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.LabelTaskBulk{}
},
}
a.POST("/tasks/:listtask/labels/bulk", bulkLabelTaskHandler.CreateWeb)
2019-09-25 20:44:41 +02:00
taskRelationHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TaskRelation{}
},
}
a.PUT("/tasks/:task/relations", taskRelationHandler.CreateWeb)
a.DELETE("/tasks/:task/relations/:relationKind/:otherTask", taskRelationHandler.DeleteWeb)
2019-09-25 20:44:41 +02:00
if config.ServiceEnableTaskAttachments.GetBool() {
taskAttachmentHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TaskAttachment{}
},
}
a.GET("/tasks/:task/attachments", taskAttachmentHandler.ReadAllWeb)
a.DELETE("/tasks/:task/attachments/:attachment", taskAttachmentHandler.DeleteWeb)
a.PUT("/tasks/:task/attachments", apiv1.UploadTaskAttachment)
a.GET("/tasks/:task/attachments/:attachment", apiv1.GetTaskAttachment)
2019-10-16 22:52:29 +02:00
}
if config.ServiceEnableTaskComments.GetBool() {
taskCommentHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TaskComment{}
},
}
a.GET("/tasks/:task/comments", taskCommentHandler.ReadAllWeb)
a.PUT("/tasks/:task/comments", taskCommentHandler.CreateWeb)
a.DELETE("/tasks/:task/comments/:commentid", taskCommentHandler.DeleteWeb)
a.POST("/tasks/:task/comments/:commentid", taskCommentHandler.UpdateWeb)
a.GET("/tasks/:task/comments/:commentid", taskCommentHandler.ReadOneWeb)
}
2018-12-31 02:18:41 +01:00
labelHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.Label{}
},
}
a.GET("/labels", labelHandler.ReadAllWeb)
a.GET("/labels/:label", labelHandler.ReadOneWeb)
a.PUT("/labels", labelHandler.CreateWeb)
a.DELETE("/labels/:label", labelHandler.DeleteWeb)
a.POST("/labels/:label", labelHandler.UpdateWeb)
2018-12-01 00:26:56 +01:00
listTeamHandler := &handler.WebHandler{
EmptyStruct: func() handler.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)
2018-12-01 00:26:56 +01:00
listUserHandler := &handler.WebHandler{
EmptyStruct: func() handler.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)
savedFiltersHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.SavedFilter{}
},
}
a.GET("/filters/:filter", savedFiltersHandler.ReadOneWeb)
a.PUT("/filters", savedFiltersHandler.CreateWeb)
a.DELETE("/filters/:filter", savedFiltersHandler.DeleteWeb)
a.POST("/filters/:filter", savedFiltersHandler.UpdateWeb)
2018-12-01 00:26:56 +01:00
namespaceHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.Namespace{}
},
}
a.GET("/namespaces", namespaceHandler.ReadAllWeb)
a.PUT("/namespaces", namespaceHandler.CreateWeb)
2018-07-21 15:08:46 +02:00
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)
2018-07-14 17:34:59 +02:00
2018-12-01 00:26:56 +01:00
namespaceTeamHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TeamNamespace{}
},
}
2018-07-18 21:54:04 +02:00
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)
2018-12-01 00:26:56 +01:00
namespaceUserHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.NamespaceUser{}
},
2018-09-04 20:15:24 +02:00
}
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)
2018-09-04 20:15:24 +02:00
2018-12-01 00:26:56 +01:00
teamHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.Team{}
},
2018-07-14 17:34:59 +02:00
}
a.GET("/teams", teamHandler.ReadAllWeb)
2018-07-21 15:08:46 +02:00
a.GET("/teams/:team", teamHandler.ReadOneWeb)
2018-07-14 17:34:59 +02:00
a.PUT("/teams", teamHandler.CreateWeb)
2018-07-21 15:08:46 +02:00
a.POST("/teams/:team", teamHandler.UpdateWeb)
a.DELETE("/teams/:team", teamHandler.DeleteWeb)
2018-07-26 09:53:32 +02:00
2018-12-01 00:26:56 +01:00
teamMemberHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.TeamMember{}
},
2018-07-26 09:53:32 +02:00
}
a.PUT("/teams/:team/members", teamMemberHandler.CreateWeb)
a.DELETE("/teams/:team/members/:user", teamMemberHandler.DeleteWeb)
a.POST("/teams/:team/members/:user/admin", teamMemberHandler.UpdateWeb)
// Subscriptions
subscriptionHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.Subscription{}
},
}
a.PUT("/subscriptions/:entity/:entityID", subscriptionHandler.CreateWeb)
a.DELETE("/subscriptions/:entity/:entityID", subscriptionHandler.DeleteWeb)
// Notifications
notificationHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject {
return &models.DatabaseNotifications{}
},
}
a.GET("/notifications", notificationHandler.ReadAllWeb)
a.POST("/notifications/:notificationid", notificationHandler.UpdateWeb)
// Migrations
m := a.Group("/migration")
registerMigrations(m)
// List Backgrounds
if config.BackgroundsEnabled.GetBool() {
a.GET("/lists/:list/background", backgroundHandler.GetListBackground)
a.DELETE("/lists/:list/background", backgroundHandler.RemoveListBackground)
if config.BackgroundsUploadEnabled.GetBool() {
uploadBackgroundProvider := &backgroundHandler.BackgroundProvider{
Provider: func() background.Provider {
return &upload.Provider{}
},
}
a.PUT("/lists/:list/backgrounds/upload", uploadBackgroundProvider.UploadBackground)
}
if config.BackgroundsUnsplashEnabled.GetBool() {
unsplashBackgroundProvider := &backgroundHandler.BackgroundProvider{
Provider: func() background.Provider {
return &unsplash.Provider{}
},
}
a.GET("/backgrounds/unsplash/search", unsplashBackgroundProvider.SearchBackgrounds)
a.POST("/lists/:list/backgrounds/unsplash", unsplashBackgroundProvider.SetBackground)
a.GET("/backgrounds/unsplash/images/:image/thumb", unsplash.ProxyUnsplashThumb)
a.GET("/backgrounds/unsplash/images/:image", unsplash.ProxyUnsplashImage)
}
}
}
func registerMigrations(m *echo.Group) {
// Wunderlist
if config.MigrationWunderlistEnable.GetBool() {
wunderlistMigrationHandler := &migrationHandler.MigrationWeb{
MigrationStruct: func() migration.Migrator {
return &wunderlist.Migration{}
},
}
wunderlistMigrationHandler.RegisterRoutes(m)
}
// Todoist
if config.MigrationTodoistEnable.GetBool() {
todoistMigrationHandler := &migrationHandler.MigrationWeb{
MigrationStruct: func() migration.Migrator {
return &todoist.Migration{}
},
}
todoistMigrationHandler.RegisterRoutes(m)
}
// Trello
if config.MigrationTrelloEnable.GetBool() {
trelloMigrationHandler := &migrationHandler.MigrationWeb{
MigrationStruct: func() migration.Migrator {
return &trello.Migration{}
},
}
trelloMigrationHandler.RegisterRoutes(m)
}
// Microsoft Todo
if config.MigrationMicrosoftTodoEnable.GetBool() {
microsoftTodoMigrationHandler := &migrationHandler.MigrationWeb{
MigrationStruct: func() migration.Migrator {
return &microsofttodo.Migration{}
},
}
microsoftTodoMigrationHandler.RegisterRoutes(m)
}
// Vikunja File Migrator
vikunjaFileMigrationHandler := &migrationHandler.FileMigratorWeb{
MigrationStruct: func() migration.FileMigrator {
return &vikunja_file.FileMigrator{}
},
}
vikunjaFileMigrationHandler.RegisterRoutes(m)
// TickTick File Migrator
tickTickFileMigrator := migrationHandler.FileMigratorWeb{
MigrationStruct: func() migration.FileMigrator {
return &ticktick.Migrator{}
},
}
tickTickFileMigrator.RegisterRoutes(m)
2018-06-10 11:11:41 +02:00
}
2019-05-22 19:48:48 +02:00
func registerCalDavRoutes(c *echo.Group) {
// Basic auth middleware
c.Use(middleware.BasicAuth(caldav.BasicAuth))
2019-05-22 19:48:48 +02:00
// THIS is the entry point for caldav clients, otherwise lists will show up double
c.Any("", caldav.EntryHandler)
c.Any("/", caldav.EntryHandler)
c.Any("/principals/*/", caldav.PrincipalHandler)
c.Any("/lists", caldav.ListHandler)
c.Any("/lists/", caldav.ListHandler)
c.Any("/lists/:list", caldav.ListHandler)
c.Any("/lists/:list/", caldav.ListHandler)
c.Any("/lists/:list/:task", caldav.TaskHandler) // Mostly used for editing
}