package api

import (
	"context"
	"path/filepath"
	"time"

	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/amqp"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/account"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/alarm"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/alarmaction"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/alarmtag"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/appinfo"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/associativetable"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/auth"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/author"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/broadcastmessage"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/colortheme"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/common"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/contextgraph"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/datastorage"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/dbexport"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/entity"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/entitybasic"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/entitycategory"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/entitycomment"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/entityinfodictionary"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/entityinfosproperty"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/entityservice"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/event"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/eventfilter"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/export"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/exportconfiguration"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/externaldatatable"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/file"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/flappingrule"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/healthcheck"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/icon"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/idlerule"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/linkrule"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/maintenance"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/messageratestats"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/middleware"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/notification"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pattern"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pbehavior"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pbehaviorcomment"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pbehaviorexception"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pbehaviorics"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pbehaviorreason"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pbehaviortimespan"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/pbehaviortype"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/permission"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/playlist"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/resolverule"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/role"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/scenario"
	apisecurity "git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/security"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/serviceweather"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/sessionauth"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/sharetoken"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/statesettings"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/techmetrics"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/template"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/user"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/userpreferences"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/view"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/viewgroup"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/viewtab"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/websocket"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/widget"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/widgetfilter"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/widgettemplate"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/api/workers"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/config"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/encoding/json"
	libentityservice "git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/entityservice"
	libevent "git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/event"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/link"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/metrics"
	libpbehavior "git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/pbehavior"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/rpc"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/statesetting"
	libtemplate "git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/template"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/template/validator"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/canopsis/usernotification"
	libfile "git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/file"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/mongo"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/postgres"
	libsecurity "git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/security"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/security/model"
	"git.canopsis.net/canopsis/canopsis-community/community/go-engines-community/lib/security/userprovider"
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"github.com/rs/zerolog"
)

const BaseUrl = "/api/v4"

const mimeTypeSvg = "image/svg+xml"

const cacheExpiration = time.Second

// RegisterRoutes
// nolint: contextcheck
func RegisterRoutes(
	ctx context.Context,
	conf config.CanopsisConf,
	router gin.IRouter,
	security Security,
	enforcer libsecurity.Enforcer,
	linkGenerator link.Generator,
	primaryDbClient mongo.DbClient,
	secondaryDbClient mongo.DbClient,
	dbExportClient mongo.DbClient,
	pgPoolProvider postgres.PoolProvider,
	amqpChannel amqp.Channel,
	lockRedisSession redis.Cmdable,
	apiConfigProvider config.ApiConfigProvider,
	timezoneConfigProvider config.TimezoneConfigProvider,
	templateConfigProvider config.TemplateConfigProvider,
	pbhEntityTypeResolver libpbehavior.EntityTypeResolver,
	pbhComputeChan chan<- rpc.PbehaviorRecomputeEvent,
	entityPublChan chan<- libentityservice.ChangeEntityMessage,
	entityCleanerTaskChan chan<- entity.CleanTask,
	exportTaskExecutor export.TaskExecutor,
	techMetricsTaskExecutor techmetrics.TaskExecutor,
	publisher amqp.Publisher,
	userInterfaceConfig config.UserInterfaceConfigProvider,
	websocketHub websocket.Hub,
	websocketStore websocket.Store,
	broadcastMessageChan chan<- bool,
	metricsEntityMetaUpdater metrics.MetaUpdater,
	metricsUserMetaUpdater metrics.MetaUpdater,
	authorProvider author.Provider,
	healthcheckStore healthcheck.Store,
	tplExecutor libtemplate.Executor,
	stateSettingsUpdatesChan chan statesetting.RuleUpdatedMessage,
	enableSameServiceNames bool,
	eventGenerator libevent.Generator,
	securityConfig libsecurity.Config,
	exdataImportWorker externaldatatable.ImportWorker,
	notifStore usernotification.Store,
	workersRunner *workers.Runner,
	logger zerolog.Logger,
) {
	sessionStore := security.GetSessionStore()
	authMiddleware := security.GetAuthMiddleware()
	security.RegisterCallbackRoutes(ctx, router, primaryDbClient, sessionStore)

	maintenanceAdapter := config.NewMaintenanceAdapter(primaryDbClient)
	userInterfaceAdapter := config.NewUserInterfaceAdapter(primaryDbClient)

	authApi := auth.NewApi(
		security.GetTokenService(),
		security.GetTokenProviders(),
		security.GetAuthProviders(),
		websocketStore,
		maintenanceAdapter,
		enforcer,
		security.GetCookieOptions().FileAccessName,
		security.GetCookieOptions().MaxAge,
		logger,
	)
	sessionauthApi := sessionauth.NewApi(
		sessionStore,
		security.GetAuthProviders(),
		maintenanceAdapter,
		enforcer,
		logger,
	)
	router.POST("/auth", sessionauthApi.LoginHandler())

	sessionProtected := router.Group("")
	{
		sessionProtected.Use(middleware.SessionAuth(primaryDbClient, apiConfigProvider, sessionStore), middleware.OnlyAuth())
		sessionProtected.GET("/logout", sessionauthApi.LogoutHandler())
	}

	unprotected := router.Group(BaseUrl)
	{
		unprotected.POST("/login", authApi.Login)
		unprotected.POST("/logout", authApi.Logout)
	}

	protected := router.Group(BaseUrl)
	{
		protected.Use(authMiddleware...)

		protected.Group("/ws").GET("", websocket.NewApi(websocketHub).Handler)

		accountRouter := protected.Group("/account/me")
		{
			accountRouter.Use(middleware.OnlyAuth())
			accountAPI := account.NewApi(account.NewStore(primaryDbClient, security.GetPasswordEncoder(), authorProvider, userInterfaceAdapter, securityConfig))
			accountRouter.GET("", accountAPI.Me)
			accountRouter.PUT("", accountAPI.Update)
		}
		protected.GET("/logged-user-count", authApi.GetLoggedUserCount)
		protected.GET("/file-access", authApi.GetFileAccess)

		userPreferencesRouter := protected.Group("/user-preferences")
		{
			userPreferencesRouter.Use(middleware.OnlyAuth())
			userPreferencesApi := userpreferences.NewApi(userpreferences.NewStore(primaryDbClient, authorProvider), widget.NewStore(primaryDbClient, authorProvider, enforcer, common.NewPatternFieldsTransformer(primaryDbClient)), enforcer)
			userPreferencesRouter.GET("/:id", userPreferencesApi.Get)
			userPreferencesRouter.PUT("", userPreferencesApi.Update)
		}

		userApi := user.NewApi(user.NewStore(primaryDbClient, security.GetPasswordEncoder(), websocketStore, authorProvider, securityConfig),
			logger, metricsUserMetaUpdater)
		userRouter := protected.Group("/users")
		{
			userRouter.POST("",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				userApi.Create,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
			userRouter.GET("",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionRead, enforcer),
				userApi.List,
			)
			userRouter.GET("/:id",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionRead, enforcer),
				userApi.Get,
			)
			userRouter.PUT("/:id",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				userApi.Update,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
			userRouter.PATCH("/:id",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				userApi.Patch,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
			userRouter.DELETE("/:id",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionDelete, enforcer),
				userApi.Delete,
			)
		}
		roleApi := role.NewApi(role.NewStore(primaryDbClient, authorProvider), logger)
		roleRouter := protected.Group("/roles")
		{
			roleRouter.POST("",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				roleApi.Create,
			)
			roleRouter.GET("",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionRead, enforcer),
				roleApi.List,
			)
			roleRouter.GET("/:id",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionRead, enforcer),
				roleApi.Get,
			)
			roleRouter.PUT("/:id",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				roleApi.Update,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
			roleRouter.DELETE("/:id",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionDelete, enforcer),
				roleApi.Delete,
			)
		}
		protected.GET("/role-templates",
			middleware.Authorize(apisecurity.PermAcl, model.PermissionRead, enforcer),
			roleApi.ListTemplates,
		)
		permissionRouter := protected.Group("/permissions")
		{
			permissionApi := permission.NewApi(permission.NewStore(primaryDbClient))
			permissionRouter.GET("",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionRead, enforcer),
				permissionApi.List,
			)
		}

		sharetokenApi := sharetoken.NewApi(sharetoken.NewStore(primaryDbClient, security.GetTokenGenerator(), authorProvider))
		sharetokenRouter := protected.Group("/share-tokens")
		{
			sharetokenRouter.POST("",
				middleware.Authorize(apisecurity.PermShareToken, model.PermissionCreate, enforcer),
				sharetokenApi.Create,
			)
			sharetokenRouter.GET("",
				middleware.Authorize(apisecurity.PermShareToken, model.PermissionRead, enforcer),
				sharetokenApi.List,
			)
			sharetokenRouter.DELETE("/:id",
				middleware.Authorize(apisecurity.PermShareToken, model.PermissionDelete, enforcer),
				sharetokenApi.Delete,
			)
		}

		alarmStore := alarm.NewStore(secondaryDbClient, dbExportClient, linkGenerator, timezoneConfigProvider, authorProvider,
			tplExecutor, json.NewDecoder(), logger)
		alarmAPI := alarm.NewApi(alarmStore, exportTaskExecutor, json.NewEncoder(), logger)
		alarmActionAPI := alarmaction.NewApi(alarmaction.NewStore(primaryDbClient, amqpChannel, canopsis.DefaultExchangeName,
			canopsis.FIFOQueueName, json.NewEncoder(), canopsis.JsonContentType, eventGenerator, logger), logger)
		alarmRouter := protected.Group("/alarms")
		{
			alarmRouter.GET(
				"",
				middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
				alarmAPI.List,
			)
			alarmRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
				alarmAPI.Get,
			)
			alarmRouter.PUT(
				"/:id/ack",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.Ack,
			)
			alarmRouter.PUT(
				"/:id/ackremove",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.AckRemove,
			)
			alarmRouter.PUT(
				"/:id/snooze",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.Snooze,
			)
			alarmRouter.PUT(
				"/:id/cancel",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.Cancel,
			)
			alarmRouter.PUT(
				"/:id/uncancel",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.Uncancel,
			)
			alarmRouter.PUT(
				"/:id/assocticket",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.AssocTicket,
			)
			alarmRouter.PUT(
				"/:id/comment",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.Comment,
			)
			alarmRouter.PUT(
				"/:id/changestate",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.ChangeState,
			)
			alarmRouter.PUT(
				"/:id/bookmark",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.AddBookmark,
			)
			alarmRouter.DELETE(
				"/:id/bookmark",
				middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
				alarmActionAPI.RemoveBookmark,
			)
		}
		protected.POST(
			"/alarm-details",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.GetDetails,
		)
		protected.GET(
			"/alarm-links/:id",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.GetLinks,
		)
		protected.GET(
			"/entityservice-alarms/:id",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.ListByService,
		)
		protected.GET(
			"/component-alarms",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.ListByComponent,
		)
		protected.GET(
			"/resolved-alarms",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.ResolvedList,
		)
		protected.GET(
			"/open-alarms",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.GetOpen,
		)
		protected.GET(
			"/alarm-counters",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.Count,
		)
		protected.GET(
			"/alarm-display-names",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmAPI.GetDisplayNames,
		)
		exportTaskExecutor.RegisterType("alarm", alarmStore.Export)
		alarmExportRouter := protected.Group("/alarm-export")
		{
			alarmExportRouter.POST(
				"",
				middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
				alarmAPI.StartExport,
			)
			alarmExportRouter.GET(
				"/:id/download",
				security.GetFileAuthMiddleware(),
				middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
				alarmAPI.DownloadExport,
			)
			alarmExportRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
				alarmAPI.GetExport,
			)
		}

		exportConfigurationAPI := exportconfiguration.NewApi(primaryDbClient, logger)
		protected.POST(
			"/export-configuration",
			middleware.Authorize(apisecurity.PermExportConfigurations, model.PermissionCan, enforcer),
			exportConfigurationAPI.Export,
		)

		entityStore := entity.NewStore(primaryDbClient, dbExportClient, timezoneConfigProvider, authorProvider, json.NewDecoder())
		entityAPI := entity.NewApi(
			entityStore,
			exportTaskExecutor,
			entityCleanerTaskChan,
			entityPublChan,
			metricsEntityMetaUpdater,
			json.NewEncoder(),
			logger,
		)

		exportTaskExecutor.RegisterType("entity", entityStore.Export)
		entityExportRouter := protected.Group("/entity-export")
		{
			entityExportRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				entityAPI.StartExport,
			)
			entityExportRouter.GET(
				"/:id/download",
				security.GetFileAuthMiddleware(),
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				entityAPI.DownloadExport,
			)
			entityExportRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				entityAPI.GetExport,
			)
		}

		protected.POST(
			"/pbehavior-timespans",
			middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
			pbehaviortimespan.GetTimeSpans(pbehaviortimespan.NewService(primaryDbClient, timezoneConfigProvider)),
		)
		protected.GET(
			"/pbehavior-ics/:id",
			middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
			pbehaviorics.GetICS(pbehaviorics.NewStore(primaryDbClient, authorProvider), pbehaviorics.NewService(timezoneConfigProvider)),
		)

		// event-filter API
		eventFilterApi := eventfilter.NewApi(
			eventfilter.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient), notifStore),
			dbexport.NewExporter(primaryDbClient),
			logger,
		)
		eventFilterRouter := protected.Group("/eventfilter/rules")
		{
			eventFilterRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				eventFilterApi.Create)
			eventFilterRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionRead, enforcer),
				eventFilterApi.Get)
			eventFilterRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionDelete, enforcer),
				eventFilterApi.Delete)
			eventFilterRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionRead, enforcer),
				eventFilterApi.List)
			eventFilterRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				eventFilterApi.Update)
		}
		protected.GET(
			"/eventfilter/:id/failures",
			middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionRead, enforcer),
			eventFilterApi.ListFailures)
		protected.PUT(
			"/eventfilter/:id/failures",
			middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionCreate, enforcer),
			eventFilterApi.ReadFailures)
		protected.POST(
			"eventfilter-db-export",
			middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionRead, enforcer),
			eventFilterApi.DBExport)

		pbehaviorApi := pbehavior.NewApi(
			pbehavior.NewStore(
				primaryDbClient,
				secondaryDbClient,
				lockRedisSession,
				pbhEntityTypeResolver,
				libpbehavior.NewTypeComputer(libpbehavior.NewModelProvider(primaryDbClient, authorProvider), json.NewDecoder()),
				timezoneConfigProvider,
				authorProvider,
				common.NewPatternFieldsTransformer(primaryDbClient),
				websocketHub,
				userInterfaceConfig,
			),
			dbexport.NewExporter(primaryDbClient),
			pbhComputeChan,
			workers.NewJobPublisher(jobKeyPbhPatterns, workersRunner),
			logger,
		)
		pbehaviorRouter := protected.Group("/pbehaviors")
		{
			pbehaviorRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				pbehaviorApi.Create)
			pbehaviorRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
				pbehaviorApi.List)
			pbehaviorRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
				pbehaviorApi.Get)
			pbehaviorRouter.GET(
				"/:id/entities",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
				pbehaviorApi.ListEntities)
			pbehaviorRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				pbehaviorApi.Update)
			pbehaviorRouter.PATCH(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				pbehaviorApi.Patch)
			pbehaviorRouter.DELETE(
				"",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionDelete, enforcer),
				pbehaviorApi.DeleteByName)
			pbehaviorRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionDelete, enforcer),
				pbehaviorApi.Delete)
		}
		protected.POST(
			"/pbehaviors-db-export",
			middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
			pbehaviorApi.DBExport,
		)
		protected.PUT(
			"/pbehavior-patterns",
			middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionUpdate, enforcer),
			pbehaviorApi.ExecPattern,
		)
		protected.PUT(
			"/all-pbehavior-patterns",
			middleware.Authorize(apisecurity.PermPbhPatterns, model.PermissionCan, enforcer),
			pbehaviorApi.ExecAllPatterns,
		)

		pbehaviorCommentRouter := protected.Group("/pbehavior-comments")
		{
			pbehaviorCommentAPI := pbehaviorcomment.NewApi(pbehaviorcomment.NewStore(primaryDbClient, authorProvider))
			pbehaviorCommentRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				pbehaviorCommentAPI.Create,
			)
			pbehaviorCommentRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionUpdate, enforcer),
				pbehaviorCommentAPI.Delete,
			)
		}
		entityRouter := protected.Group("/entities")
		{
			entityRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				entityAPI.List,
			)

			entityRouter.POST(
				"/archive-disabled",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionDelete, enforcer),
				entityAPI.ArchiveDisabled,
			)
			entityRouter.POST(
				"/archive-unlinked",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionDelete, enforcer),
				entityAPI.ArchiveUnlinked,
			)
			entityRouter.POST(
				"/clean-archived",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionDelete, enforcer),
				entityAPI.CleanArchived,
			)

			entityRouter.GET(
				"/context-graph",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				entityAPI.GetContextGraph,
			)

			entityRouter.POST(
				"/check-state-setting",
				middleware.Authorize(apisecurity.ObjStateSettings, model.PermissionRead, enforcer),
				entityAPI.CheckStateSetting,
			)

			entityRouter.GET(
				"/state-setting",
				middleware.Authorize(apisecurity.ObjStateSettings, model.PermissionRead, enforcer),
				entityAPI.GetStateSetting,
			)

			entityRouter.GET(
				"/pbehaviors",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
				pbehaviorApi.ListByEntityID,
			)

			entityRouter.GET(
				"/pbehavior-calendar",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionRead, enforcer),
				pbehaviorApi.CalendarByEntityID,
			)
		}

		entitybasicsAPI := entitybasic.NewApi(entitybasic.NewStore(primaryDbClient), entityPublChan, metricsEntityMetaUpdater, logger)
		entitybasicsRouter := protected.Group("/entitybasics")
		{
			entitybasicsRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				entitybasicsAPI.Get,
			)
			entitybasicsRouter.PUT(
				"",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				entitybasicsAPI.Update,
			)
			entitybasicsRouter.DELETE(
				"",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionDelete, enforcer),
				entitybasicsAPI.Delete,
			)
		}

		entityserviceStore := entityservice.NewStore(primaryDbClient, linkGenerator, enableSameServiceNames, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient), logger)
		entityserviceAPI := entityservice.NewApi(entityserviceStore, entityPublChan, metricsEntityMetaUpdater, logger)
		entityserviceRouter := protected.Group("/entityservices")
		{
			entityserviceRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				entityserviceAPI.Create,
			)
			entityserviceRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionRead, enforcer),
				entityserviceAPI.Get,
			)
			entityserviceRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				entityserviceAPI.Update,
			)
			entityserviceRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionDelete, enforcer),
				entityserviceAPI.Delete,
			)
			protected.GET(
				"/entityservice-dependencies",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionRead, enforcer),
				entityserviceAPI.GetDependencies,
			)
			protected.GET(
				"/entityservice-impacts",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionRead, enforcer),
				entityserviceAPI.GetImpacts,
			)
		}

		entityCommentRouter := protected.Group("/entity-comments")
		{
			entityCommentAPI := entitycomment.NewApi(entitycomment.NewStore(primaryDbClient, logger))
			entityCommentRouter.POST(
				"",
				middleware.Authorize(apisecurity.PermEntityComment, model.PermissionCan, enforcer),
				middleware.SetAuthor(),
				entityCommentAPI.Create,
			)
			entityCommentRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.PermEntityComment, model.PermissionCan, enforcer),
				middleware.SetAuthor(),
				entityCommentAPI.Update,
			)
			entityCommentRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
				entityCommentAPI.List,
			)
		}

		typeRouter := protected.Group("/pbehavior-types")
		{
			pbehaviorTypeApi := pbehaviortype.NewApi(
				pbehaviortype.NewStore(primaryDbClient, authorProvider),
				pbhComputeChan,
				logger,
			)
			pbhTypeAuthorizeRead := middleware.Authorize(apisecurity.ObjPbehaviorType, model.PermissionRead, enforcer)
			pbhTypeAuthorizeCreate := middleware.Authorize(apisecurity.ObjPbehaviorType, model.PermissionCreate, enforcer)
			pbhTypeAuthorizeUpdate := middleware.Authorize(apisecurity.ObjPbehaviorType, model.PermissionUpdate, enforcer)
			pbhTypeAuthorizeDelete := middleware.Authorize(apisecurity.ObjPbehaviorType, model.PermissionDelete, enforcer)

			typeRouter.GET("", pbhTypeAuthorizeRead, pbehaviorTypeApi.List)
			typeRouter.POST("", pbhTypeAuthorizeCreate, middleware.SetAuthor(), pbehaviorTypeApi.Create)
			typeRouter.GET("/next-priority", pbhTypeAuthorizeRead, pbehaviorTypeApi.GetNextPriority)
			typeRouter.GET("/:id", pbhTypeAuthorizeRead, pbehaviorTypeApi.Get)
			typeRouter.PUT("/:id", pbhTypeAuthorizeUpdate, middleware.SetAuthor(), pbehaviorTypeApi.Update)
			typeRouter.DELETE("/:id", pbhTypeAuthorizeDelete, pbehaviorTypeApi.Delete)
		}

		reasonRouter := protected.Group("/pbehavior-reasons")
		{
			reasonAPI := pbehaviorreason.NewApi(
				pbehaviorreason.NewStore(primaryDbClient, authorProvider),
				pbhComputeChan,
				logger,
			)
			reasonRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjPbehaviorReason, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				reasonAPI.Create)
			reasonRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjPbehaviorReason, model.PermissionRead, enforcer),
				reasonAPI.List)
			reasonRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehaviorReason, model.PermissionRead, enforcer),
				reasonAPI.Get)
			reasonRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehaviorReason, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				reasonAPI.Update)
			reasonRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehaviorReason, model.PermissionDelete, enforcer),
				reasonAPI.Delete)
		}
		exceptionAPI := pbehaviorexception.NewApi(
			pbehaviorexception.NewStore(primaryDbClient, timezoneConfigProvider, authorProvider),
			pbhComputeChan,
			conf.File.ImportMaxSize,
			logger,
		)
		exceptionRouter := protected.Group("/pbehavior-exceptions")
		{
			exceptionRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjPbehaviorException, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				exceptionAPI.Create)
			exceptionRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjPbehaviorException, model.PermissionRead, enforcer),
				exceptionAPI.List)
			exceptionRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehaviorException, model.PermissionRead, enforcer),
				exceptionAPI.Get)
			exceptionRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehaviorException, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				exceptionAPI.Update)
			exceptionRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjPbehaviorException, model.PermissionDelete, enforcer),
				exceptionAPI.Delete)
		}

		protected.POST(
			"/pbehavior-exception-import",
			middleware.Authorize(apisecurity.ObjPbehaviorException, model.PermissionCreate, enforcer),
			exceptionAPI.Import,
		)

		weatherRouter := protected.Group("/weather-services")
		{
			weatherAPI := serviceweather.NewApi(serviceweather.NewStore(
				primaryDbClient,
				linkGenerator,
				alarmStore,
				timezoneConfigProvider,
				authorProvider,
				logger,
			))
			weatherRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionRead, enforcer),
				weatherAPI.List,
			)
			weatherRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityService, model.PermissionRead, enforcer),
				weatherAPI.EntityList,
			)
		}

		entityCategoryRouter := protected.Group("/entity-categories")
		{
			entityCategoryAPI := entitycategory.NewApi(entitycategory.NewStore(primaryDbClient, authorProvider))
			entityCategoryRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjEntityCategory, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				entityCategoryAPI.Create,
			)
			entityCategoryRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjEntityCategory, model.PermissionRead, enforcer),
				entityCategoryAPI.List,
			)
			entityCategoryRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityCategory, model.PermissionRead, enforcer),
				entityCategoryAPI.Get,
			)
			entityCategoryRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityCategory, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				entityCategoryAPI.Update,
			)
			entityCategoryRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityCategory, model.PermissionDelete, enforcer),
				entityCategoryAPI.Delete,
			)
		}

		eventApi := event.NewApi(publisher, logger)
		eventRouter := protected.Group("/event")
		{
			eventRouter.POST(
				"",
				middleware.Authorize(apisecurity.PermEvent, model.PermissionCan, enforcer),
				eventApi.Send)
		}

		appInfoApi := appinfo.NewApi(appinfo.NewStore(primaryDbClient, maintenanceAdapter, pgPoolProvider, security.GetConfig(), authorProvider))
		protected.GET("app-info", appInfoApi.GetAppInfo)
		appInfoRouter := protected.Group("/internal")
		{
			appInfoRouter.PUT(
				"user_interface",
				middleware.Authorize(apisecurity.PermUserInterfaceUpdate, model.PermissionCan, enforcer),
				appInfoApi.UpdateUserInterface,
			)
			appInfoRouter.POST(
				"user_interface",
				middleware.Authorize(apisecurity.PermUserInterfaceUpdate, model.PermissionCan, enforcer),
				appInfoApi.UpdateUserInterface,
			)
		}

		viewAPI := view.NewApi(
			view.NewStore(
				primaryDbClient,
				viewtab.NewStore(primaryDbClient, widget.NewStore(primaryDbClient, authorProvider, enforcer, common.NewPatternFieldsTransformer(primaryDbClient)), authorProvider, enforcer),
				authorProvider,
				enforcer,
			),
			enforcer,
		)
		viewRouter := protected.Group("/views")
		{
			viewRouter.POST(
				"",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionCreate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.SetAuthor(),
				viewAPI.Create,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
			viewRouter.GET(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewViewOwnerStrategy(primaryDbClient, enforcer, model.PermissionRead)),
				viewAPI.Get,
			)
			viewRouter.PUT(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionUpdate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewViewOwnerStrategy(primaryDbClient, enforcer, model.PermissionUpdate)),
				middleware.SetAuthor(),
				viewAPI.Update,
			)
			viewRouter.DELETE(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionDelete,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewViewOwnerStrategy(primaryDbClient, enforcer, model.PermissionDelete)),
				viewAPI.Delete,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
		}

		viewTabAPI := viewtab.NewApi(viewtab.NewStore(primaryDbClient, widget.NewStore(primaryDbClient, authorProvider, enforcer, common.NewPatternFieldsTransformer(primaryDbClient)), authorProvider, enforcer), enforcer)
		viewTabRouter := protected.Group("/view-tabs")
		{
			viewTabRouter.POST(
				"",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionUpdate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.SetAuthor(),
				viewTabAPI.Create,
			)
			viewTabRouter.GET(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewViewTabOwnershipStrategy(primaryDbClient, enforcer, model.PermissionRead)),
				viewTabAPI.Get,
			)
			viewTabRouter.PUT(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionUpdate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewViewTabOwnershipStrategy(primaryDbClient, enforcer, model.PermissionUpdate)),
				middleware.SetAuthor(),
				viewTabAPI.Update,
			)
			viewTabRouter.DELETE(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionUpdate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewViewTabOwnershipStrategy(primaryDbClient, enforcer, model.PermissionUpdate)),
				viewTabAPI.Delete,
			)
		}

		widgetAPI := widget.NewApi(
			widget.NewStore(primaryDbClient, authorProvider, enforcer, common.NewPatternFieldsTransformer(primaryDbClient)),
			enforcer,
		)
		widgetRouter := protected.Group("/widgets")
		{
			widgetRouter.POST(
				"",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionUpdate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.SetAuthor(),
				widgetAPI.Create,
			)
			widgetRouter.GET(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewWidgetOwnershipStrategy(primaryDbClient, enforcer, model.PermissionRead)),
				widgetAPI.Get,
			)
			widgetRouter.PUT(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionUpdate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewWidgetOwnershipStrategy(primaryDbClient, enforcer, model.PermissionUpdate)),
				middleware.SetAuthor(),
				widgetAPI.Update,
			)
			widgetRouter.DELETE(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionUpdate,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				middleware.AuthorizeOwnership(apisecurity.NewWidgetOwnershipStrategy(primaryDbClient, enforcer, model.PermissionUpdate)),
				widgetAPI.Delete,
			)
		}

		widgetFilterAPI := widgetfilter.NewApi(widgetfilter.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient)), enforcer)
		widgetFilterRouter := protected.Group("/widget-filters")
		{
			widgetFilterRouter.GET(
				"",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				widgetFilterAPI.List,
			)
			widgetFilterRouter.POST(
				"",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer), // keep PermissionRead for private filters
				middleware.SetAuthor(),
				widgetFilterAPI.Create,
			)
			widgetFilterRouter.GET(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				widgetFilterAPI.Get,
			)
			widgetFilterRouter.PUT(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer), // keep PermissionRead for private filters
				middleware.SetAuthor(),
				widgetFilterAPI.Update,
			)
			widgetFilterRouter.DELETE(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjView,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				widgetFilterAPI.Delete,
			)
		}

		protected.PUT(
			"/widget-filter-positions",
			middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
				{
					Obj: apisecurity.ObjView,
					Act: model.PermissionRead,
				},
				{
					Obj: apisecurity.PermPrivateViewGroups,
					Act: model.PermissionCan,
				},
			}, enforcer),
			widgetFilterAPI.UpdatePositions,
		)

		widgetTemplateAPI := widgettemplate.NewApi(widgettemplate.NewStore(primaryDbClient, authorProvider))
		widgetTemplateRouter := protected.Group("/widget-templates")
		{
			widgetTemplateRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjWidgetTemplate, model.PermissionRead, enforcer),
				widgetTemplateAPI.List,
			)
			widgetTemplateRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjWidgetTemplate, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				widgetTemplateAPI.Create,
			)
			widgetTemplateRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjWidgetTemplate, model.PermissionRead, enforcer),
				widgetTemplateAPI.Get,
			)
			widgetTemplateRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjWidgetTemplate, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				widgetTemplateAPI.Update,
			)
			widgetTemplateRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjWidgetTemplate, model.PermissionDelete, enforcer),
				widgetTemplateAPI.Delete,
			)
		}

		viewGroupAPI := viewgroup.NewApi(viewgroup.NewStore(primaryDbClient, authorProvider))
		viewGroupRouter := protected.Group("/view-groups")
		{
			viewGroupRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjViewGroup, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				viewGroupAPI.Create,
			)
			viewGroupRouter.GET(
				"",
				middleware.ProvideAuthorizedIds(model.PermissionRead, enforcer, apisecurity.NewViewOwnedObjectsProvider(primaryDbClient)),
				viewGroupAPI.List,
			)
			viewGroupRouter.GET(
				"/:id",
				middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
					{
						Obj: apisecurity.ObjViewGroup,
						Act: model.PermissionRead,
					},
					{
						Obj: apisecurity.PermPrivateViewGroups,
						Act: model.PermissionCan,
					},
				}, enforcer),
				viewGroupAPI.Get,
			)
			viewGroupRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjViewGroup, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				viewGroupAPI.Update,
			)
			viewGroupRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjViewGroup, model.PermissionDelete, enforcer),
				viewGroupAPI.Delete,
			)
		}

		protected.POST(
			"/view-copy/:id",
			middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
				{
					Obj: apisecurity.ObjView,
					Act: model.PermissionUpdate,
				},
				{
					Obj: apisecurity.PermPrivateViewGroups,
					Act: model.PermissionCan,
				},
			}, enforcer),
			middleware.AuthorizeOwnership(apisecurity.NewViewOwnerStrategy(primaryDbClient, enforcer, model.PermissionRead)),
			middleware.SetAuthor(),
			viewAPI.Copy,
			middleware.ReloadEnforcerPolicyOnChange(enforcer),
		)

		protected.PUT(
			"/view-positions",
			middleware.Authorize(apisecurity.ObjView, model.PermissionUpdate, enforcer),
			middleware.Authorize(apisecurity.ObjViewGroup, model.PermissionUpdate, enforcer),
			viewAPI.UpdatePositions,
		)

		protected.POST(
			"/view-export",
			middleware.Authorize(apisecurity.ObjView, model.PermissionRead, enforcer),
			middleware.Authorize(apisecurity.ObjViewGroup, model.PermissionRead, enforcer),
			viewAPI.Export,
		)

		protected.POST(
			"/view-import",
			middleware.Authorize(apisecurity.ObjView, model.PermissionUpdate, enforcer),
			middleware.Authorize(apisecurity.ObjViewGroup, model.PermissionUpdate, enforcer),
			viewAPI.Import,
			middleware.ReloadEnforcerPolicyOnChange(enforcer),
		)

		protected.POST(
			"/view-tab-copy/:id",
			middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
				{
					Obj: apisecurity.ObjView,
					Act: model.PermissionUpdate,
				},
				{
					Obj: apisecurity.PermPrivateViewGroups,
					Act: model.PermissionCan,
				},
			}, enforcer),
			middleware.AuthorizeOwnership(apisecurity.NewViewTabOwnershipStrategy(primaryDbClient, enforcer, model.PermissionRead)),
			middleware.SetAuthor(),
			viewTabAPI.Copy,
		)

		protected.PUT(
			"/view-tab-positions",
			middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
				{
					Obj: apisecurity.ObjView,
					Act: model.PermissionUpdate,
				},
				{
					Obj: apisecurity.PermPrivateViewGroups,
					Act: model.PermissionCan,
				},
			}, enforcer),
			viewTabAPI.UpdatePositions,
		)

		protected.POST(
			"/widget-copy/:id",
			middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
				{
					Obj: apisecurity.ObjView,
					Act: model.PermissionUpdate,
				},
				{
					Obj: apisecurity.PermPrivateViewGroups,
					Act: model.PermissionCan,
				},
			}, enforcer),
			middleware.AuthorizeOwnership(apisecurity.NewWidgetOwnershipStrategy(primaryDbClient, enforcer, model.PermissionRead)),
			middleware.SetAuthor(),
			widgetAPI.Copy,
		)

		protected.PUT(
			"/widget-grid-positions",
			middleware.AuthorizeAtLeastOnePerm([]apisecurity.PermCheck{
				{
					Obj: apisecurity.ObjView,
					Act: model.PermissionUpdate,
				},
				{
					Obj: apisecurity.PermPrivateViewGroups,
					Act: model.PermissionCan,
				},
			}, enforcer),
			widgetAPI.UpdateGridPositions,
		)

		// broadcast message API
		broadcastMessageApi := broadcastmessage.NewApi(
			broadcastmessage.NewStore(primaryDbClient, maintenanceAdapter, authorProvider),
			broadcastMessageChan,
		)
		broadcastMessageRouter := protected.Group("/broadcast-message")
		{

			broadcastMessageRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjBroadcastMessage, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				broadcastMessageApi.Create)
			broadcastMessageRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjBroadcastMessage, model.PermissionRead, enforcer),
				broadcastMessageApi.Get)
			broadcastMessageRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjBroadcastMessage, model.PermissionDelete, enforcer),
				broadcastMessageApi.Delete)
			broadcastMessageRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjBroadcastMessage, model.PermissionRead, enforcer),
				broadcastMessageApi.List)
			broadcastMessageRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjBroadcastMessage, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				broadcastMessageApi.Update)
			// can not make typical format like /api/v4/broadcast-message/active
			// because it would be failed with conflict error apart of get /:id route
			router.GET(BaseUrl+"/active-broadcast-message", broadcastMessageApi.GetActive)
		}

		associativeTableApi := associativetable.NewApi(associativetable.NewStore(primaryDbClient))
		associativeRouter := protected.Group("/associativetable")
		{
			associativeRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjAssociativeTable, model.PermissionUpdate, enforcer),
				associativeTableApi.Update,
			)
			associativeRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjAssociativeTable, model.PermissionRead, enforcer),
				associativeTableApi.Get,
			)
			associativeRouter.DELETE(
				"",
				middleware.Authorize(apisecurity.ObjAssociativeTable, model.PermissionDelete, enforcer),
				associativeTableApi.Delete,
			)
		}

		scenarioAPI := scenario.NewApi(
			scenario.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient)),
			dbexport.NewExporter(primaryDbClient),
			logger,
		)
		scenarioRouter := protected.Group("/scenarios")
		{
			scenarioRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjAction, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				scenarioAPI.Create,
			)
			scenarioRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjAction, model.PermissionRead, enforcer),
				scenarioAPI.List,
			)
			scenarioRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjAction, model.PermissionRead, enforcer),
				scenarioAPI.Get,
			)
			scenarioRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjAction, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				scenarioAPI.Update,
			)
			scenarioRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjAction, model.PermissionDelete, enforcer),
				scenarioAPI.Delete,
			)
		}
		protected.POST(
			"scenarios-db-export",
			middleware.Authorize(apisecurity.ObjAction, model.PermissionRead, enforcer),
			scenarioAPI.DBExport)

		contextGraphAPI := contextgraph.NewApi(conf, contextgraph.NewMongoStatusReporter(primaryDbClient),
			workers.NewJobPublisher(jobKeyImport, workersRunner), conf.File.ImportMaxSize, logger)
		protected.PUT(
			"contextgraph-import",
			middleware.Authorize(apisecurity.ObjContextGraph, model.PermissionCreate, enforcer),
			contextGraphAPI.ImportAll,
		)
		protected.PUT(
			"contextgraph-import-partial",
			middleware.Authorize(apisecurity.ObjContextGraph, model.PermissionCreate, enforcer),
			contextGraphAPI.ImportPartial,
		)
		protected.GET(
			"contextgraph-import-status/:id",
			middleware.Authorize(apisecurity.ObjContextGraph, model.PermissionRead, enforcer),
			contextGraphAPI.Status,
		)

		stateSettingsRouter := protected.Group("/state-settings")
		{
			stateSettingsApi := statesettings.NewApi(statesettings.NewStore(primaryDbClient, stateSettingsUpdatesChan, authorProvider))
			stateSettingsRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjStateSettings, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				stateSettingsApi.Create,
			)
			stateSettingsRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjStateSettings, model.PermissionRead, enforcer),
				stateSettingsApi.List,
			)
			stateSettingsRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjStateSettings, model.PermissionRead, enforcer),
				stateSettingsApi.Get,
			)
			stateSettingsRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjStateSettings, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				stateSettingsApi.Update,
			)
			stateSettingsRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjStateSettings, model.PermissionDelete, enforcer),
				stateSettingsApi.Delete,
			)
		}

		notificationAPI := notification.NewApi(notification.NewStore(primaryDbClient, authorProvider))
		protected.GET("/notifications", notificationAPI.List)
		notifSettingsRouter := protected.Group("/notification-settings")
		{
			notifSettingsRouter.PUT(
				"",
				middleware.Authorize(apisecurity.PermNotification, model.PermissionCan, enforcer),
				middleware.SetAuthor(),
				notificationAPI.UpdateSettings,
			)
			notifSettingsRouter.GET(
				"",
				middleware.Authorize(apisecurity.PermNotification, model.PermissionCan, enforcer),
				notificationAPI.GetSettings,
			)
		}

		playlistRouter := protected.Group("/playlists")
		{
			playlistApi := playlist.NewApi(playlist.NewStore(primaryDbClient, authorProvider), viewtab.NewStore(primaryDbClient, widget.NewStore(primaryDbClient, authorProvider, enforcer, common.NewPatternFieldsTransformer(primaryDbClient)), authorProvider, enforcer), enforcer)
			playlistRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjPlaylist, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				playlistApi.Create,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
			playlistRouter.GET(
				"",
				middleware.ProvideAuthorizedIds(model.PermissionRead, enforcer, nil),
				playlistApi.List,
			)
			playlistRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjPlaylist, model.PermissionRead, enforcer),
				middleware.AuthorizeByID(model.PermissionRead, enforcer),
				playlistApi.Get,
			)
			playlistRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjPlaylist, model.PermissionUpdate, enforcer),
				middleware.AuthorizeByID(model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				playlistApi.Update,
			)
			playlistRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjPlaylist, model.PermissionDelete, enforcer),
				middleware.AuthorizeByID(model.PermissionDelete, enforcer),
				playlistApi.Delete,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)
		}

		idleRuleStore := idlerule.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient))
		idleRuleAPI := idlerule.NewApi(idleRuleStore, dbexport.NewExporter(primaryDbClient), logger)
		idleRuleRouter := protected.Group("/idle-rules")
		{
			idleRuleRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				idleRuleAPI.Create,
			)
			idleRuleRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionRead, enforcer),
				idleRuleAPI.List,
			)
			idleRuleRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionRead, enforcer),
				idleRuleAPI.Get,
			)
			idleRuleRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				idleRuleAPI.Update,
			)
			idleRuleRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionDelete, enforcer),
				idleRuleAPI.Delete,
			)
		}
		protected.POST(
			"idle-rules-db-export",
			middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionRead, enforcer),
			idleRuleAPI.DBExport)

		patternAPI := pattern.NewApi(pattern.NewStore(primaryDbClient, secondaryDbClient, pbhComputeChan, entityPublChan, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient), logger),
			userInterfaceConfig, enforcer, logger)
		patternRouter := protected.Group("/patterns")
		{
			patternRouter.Use(middleware.OnlyAuth())
			patternRouter.POST(
				"",
				middleware.SetAuthor(),
				patternAPI.Create,
			)
			patternRouter.GET(
				"",
				patternAPI.List,
			)
			patternRouter.GET(
				"/:id",
				patternAPI.Get,
			)
			patternRouter.PUT(
				"/:id",
				middleware.SetAuthor(),
				patternAPI.Update,
			)
			patternRouter.DELETE(
				"/:id",
				patternAPI.Delete,
			)
		}
		protected.POST(
			"/patterns-alarms-count",
			middleware.OnlyAuth(),
			patternAPI.CountAlarms,
		)
		protected.POST(
			"/patterns-entities-count",
			middleware.OnlyAuth(),
			patternAPI.CountEntities,
		)

		linkRuleAPI := linkrule.NewApi(
			linkrule.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient)),
			dbexport.NewExporter(primaryDbClient),
			logger,
		)
		linkRuleRouter := protected.Group("/link-rules")
		{
			linkRuleRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				linkRuleAPI.Create,
			)
			linkRuleRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionRead, enforcer),
				linkRuleAPI.List,
			)
			linkRuleRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionRead, enforcer),
				linkRuleAPI.Get,
			)
			linkRuleRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				linkRuleAPI.Update,
			)
			linkRuleRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionDelete, enforcer),
				linkRuleAPI.Delete,
			)
		}
		protected.POST(
			"link-rules-db-export",
			middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionRead, enforcer),
			linkRuleAPI.DBExport)

		linkCategoryRouter := protected.Group("/link-categories")
		{
			linkCategoryRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionRead, enforcer),
				linkRuleAPI.GetCategories,
			)
		}

		alarmTagAPI := alarmtag.NewApi(
			alarmtag.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient)),
			logger,
		)
		alarmTagRouter := protected.Group("/alarm-tags")
		{
			alarmTagRouter.GET(
				"",
				middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
				alarmTagAPI.List,
			)
			alarmTagRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjAlarmTag, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				alarmTagAPI.Create,
			)
			alarmTagRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjAlarmTag, model.PermissionRead, enforcer),
				alarmTagAPI.Get,
			)
			alarmTagRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjAlarmTag, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				alarmTagAPI.Update,
			)
			alarmTagRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjAlarmTag, model.PermissionDelete, enforcer),
				alarmTagAPI.Delete,
			)
		}
		protected.GET(
			"alarm-tag-labels",
			middleware.Authorize(apisecurity.PermAlarmRead, model.PermissionCan, enforcer),
			alarmTagAPI.ListLabels,
		)

		colorThemeApi := colortheme.NewApi(colortheme.NewStore(primaryDbClient, authorProvider, userInterfaceAdapter), logger)
		colorThemeRouter := protected.Group("/color-themes")
		{
			colorThemeRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjColorTheme, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				colorThemeApi.Create,
			)
			colorThemeRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjColorTheme, model.PermissionRead, enforcer),
				colorThemeApi.List,
			)
			colorThemeRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjColorTheme, model.PermissionRead, enforcer),
				colorThemeApi.Get,
			)
			colorThemeRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjColorTheme, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				colorThemeApi.Update,
			)
			colorThemeRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjColorTheme, model.PermissionDelete, enforcer),
				colorThemeApi.Delete,
			)
		}

		healthcheckRouter := protected.Group("/healthcheck")
		{
			healthcheckApi := healthcheck.NewApi(healthcheckStore)
			healthcheckRouter.GET(
				"",
				middleware.Authorize(apisecurity.PermHealthcheck, model.PermissionCan, enforcer),
				healthcheckApi.Get,
			)
			healthcheckRouter.GET(
				"/live",
				healthcheckApi.IsLive,
			)
			healthcheckRouter.GET(
				"/status",
				middleware.Authorize(apisecurity.PermHealthcheck, model.PermissionCan, enforcer),
				healthcheckApi.GetStatus,
			)
			healthcheckRouter.GET(
				"/engines-order",
				middleware.Authorize(apisecurity.PermHealthcheck, model.PermissionCan, enforcer),
				healthcheckApi.GetEnginesOrder,
			)
			healthcheckRouter.GET(
				"/parameters",
				middleware.Authorize(apisecurity.PermHealthcheck, model.PermissionCan, enforcer),
				healthcheckApi.GetParameters,
			)
			healthcheckRouter.PUT(
				"/parameters",
				middleware.Authorize(apisecurity.PermHealthcheck, model.PermissionCan, enforcer),
				healthcheckApi.UpdateParameters,
			)
		}

		externalDataStore := externaldatatable.NewStore(primaryDbClient, pgPoolProvider, dbExportClient, json.NewDecoder())
		externalDataTableAPI := externaldatatable.NewAPI(externalDataStore, exdataImportWorker,
			conf.File.ImportMaxSize, exportTaskExecutor, json.NewEncoder(), logger)
		externalDataTableRouter := protected.Group("/external-data-tables")
		{
			externalDataTableRouter.POST(
				"/:table/data",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				externalDataTableAPI.CreateData,
			)
			externalDataTableRouter.GET(
				"/:table/data",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.ListData,
			)
			externalDataTableRouter.GET(
				"/:table/data/:id",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.GetData,
			)
			externalDataTableRouter.PUT(
				"/:table/data/:id",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				externalDataTableAPI.UpdateData,
			)
			externalDataTableRouter.DELETE(
				"/:table/data/:id",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				externalDataTableAPI.DeleteData,
			)
			externalDataTableRouter.GET(
				"/:table/schema",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.GetSchema,
			)
			externalDataTableRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				externalDataTableAPI.Create,
			)
			externalDataTableRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.List,
			)
			externalDataTableRouter.GET(
				"/:table",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.Get,
			)
			externalDataTableRouter.PUT(
				"/:table",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				externalDataTableAPI.Update,
			)
			externalDataTableRouter.DELETE(
				"/:table",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionDelete, enforcer),
				externalDataTableAPI.Delete,
			)
		}

		externalDataImportRouter := protected.Group("/external-data-import")
		{
			externalDataImportRouter.POST(
				"/:table",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				externalDataTableAPI.Import,
			)
			externalDataImportRouter.GET(
				"/:id/status",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				externalDataTableAPI.ImportStatus,
			)
			externalDataImportRouter.GET(
				"/:id/data",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				externalDataTableAPI.ImportData,
			)
			externalDataImportRouter.PUT(
				"/:id/complete",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				externalDataTableAPI.ImportComplete,
			)
		}

		exportTaskExecutor.RegisterType("externaldata", externalDataStore.Export)
		externalDataExportRouter := protected.Group("/external-data-export")
		{
			externalDataExportRouter.POST(
				":table",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.Export,
			)
			externalDataExportRouter.GET(
				"/:id/download",
				security.GetFileAuthMiddleware(),
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.ExportDownload,
			)
			externalDataExportRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionRead, enforcer),
				externalDataTableAPI.ExportStatus,
			)
		}

		bulkRouter := protected.Group("/bulk")
		{
			patternRouter := bulkRouter.Group("/patterns")
			{
				patternRouter.DELETE(
					"",
					middleware.PreProcessBulk(apiConfigProvider, false),
					patternAPI.BulkDelete,
				)
			}

			scenarioRouter := bulkRouter.Group("/scenarios")
			{
				scenarioRouter.POST(
					"",
					middleware.Authorize(apisecurity.ObjAction, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					scenarioAPI.BulkCreate,
				)
				scenarioRouter.PUT(
					"",
					middleware.Authorize(apisecurity.ObjAction, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					scenarioAPI.BulkUpdate,
				)
				scenarioRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjAction, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					scenarioAPI.BulkDelete,
				)
			}

			idleruleRouter := bulkRouter.Group("/idle-rules")
			{
				idleruleRouter.POST(
					"",
					middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					idleRuleAPI.BulkCreate,
				)
				idleruleRouter.PUT(
					"",
					middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					idleRuleAPI.BulkUpdate,
				)
				idleruleRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjIdleRule, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					idleRuleAPI.BulkDelete,
				)
			}

			eventFilterRouter := bulkRouter.Group("/eventfilters")
			{
				eventFilterRouter.POST(
					"",
					middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					eventFilterApi.BulkCreate,
				)
				eventFilterRouter.PUT(
					"",
					middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					eventFilterApi.BulkUpdate,
				)
				eventFilterRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjEventFilter, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					eventFilterApi.BulkDelete,
				)
			}

			entityserviceRouter := bulkRouter.Group("/entityservices")
			{
				entityserviceRouter.POST(
					"",
					middleware.Authorize(apisecurity.ObjEntityService, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					entityserviceAPI.BulkCreate,
				)
				entityserviceRouter.PUT(
					"",
					middleware.Authorize(apisecurity.ObjEntityService, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					entityserviceAPI.BulkUpdate,
				)
				entityserviceRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjEntityService, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					entityserviceAPI.BulkDelete,
				)
			}

			bulkRouter.PUT(
				"/role-permissions",
				middleware.Authorize(apisecurity.PermAcl, model.PermissionUpdate, enforcer),
				middleware.PreProcessBulk(apiConfigProvider, false),
				roleApi.BulkUpdatePermissions,
				middleware.ReloadEnforcerPolicyOnChange(enforcer),
			)

			userRouter := bulkRouter.Group("/users")
			{
				userRouter.POST(
					"",
					middleware.Authorize(apisecurity.PermAcl, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					userApi.BulkCreate,
					middleware.ReloadEnforcerPolicyOnChange(enforcer),
				)
				userRouter.PUT(
					"",
					middleware.Authorize(apisecurity.PermAcl, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					userApi.BulkUpdate,
					middleware.ReloadEnforcerPolicyOnChange(enforcer),
				)
				userRouter.PATCH(
					"",
					middleware.Authorize(apisecurity.PermAcl, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					userApi.BulkPatch,
					middleware.ReloadEnforcerPolicyOnChange(enforcer),
				)
				userRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.PermAcl, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					userApi.BulkDelete,
				)
			}

			pbehaviorRouter := bulkRouter.Group("/pbehaviors")
			{
				pbehaviorRouter.POST(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					pbehaviorApi.BulkCreate,
				)
				pbehaviorRouter.PUT(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					pbehaviorApi.BulkUpdate,
				)
				pbehaviorRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					pbehaviorApi.BulkDelete,
				)
			}

			entityPbehaviorRouter := bulkRouter.Group("/entity-pbehaviors")
			{
				entityPbehaviorRouter.POST(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					pbehaviorApi.BulkEntityCreate,
				)
				entityPbehaviorRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					pbehaviorApi.BulkEntityDelete,
				)
			}

			connectorPbehaviorRouter := bulkRouter.Group("/connector-pbehaviors")
			{
				connectorPbehaviorRouter.POST(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionCreate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					pbehaviorApi.BulkConnectorCreate,
				)
				connectorPbehaviorRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					pbehaviorApi.BulkConnectorDelete,
				)
				connectorPbehaviorRouter.PUT(
					"",
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionCreate, enforcer),
					middleware.Authorize(apisecurity.ObjPbehavior, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					pbehaviorApi.BulkConnectorEdit,
				)
			}

			entityRouter := bulkRouter.Group("/entities")
			{
				entityRouter.PUT(
					"/enable",
					middleware.Authorize(apisecurity.ObjEntity, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					entityAPI.BulkEnable,
				)
				entityRouter.PUT(
					"/disable",
					middleware.Authorize(apisecurity.ObjEntity, model.PermissionUpdate, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, true),
					entityAPI.BulkDisable,
				)
			}

			linkRuleRouter := bulkRouter.Group("/link-rules")
			{
				linkRuleRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjLinkRule, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					linkRuleAPI.BulkDelete,
				)
			}

			alarmRouter := bulkRouter.Group("alarms")
			{
				alarmRouter.PUT(
					"/ack",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkAck,
				)
				alarmRouter.PUT(
					"/ackremove",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkAckRemove,
				)
				alarmRouter.PUT(
					"/snooze",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkSnooze,
				)
				alarmRouter.PUT(
					"/cancel",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkCancel,
				)
				alarmRouter.PUT(
					"/uncancel",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkUncancel,
				)
				alarmRouter.PUT(
					"/assocticket",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkAssocTicket,
				)
				alarmRouter.PUT(
					"/comment",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkComment,
				)
				alarmRouter.PUT(
					"/changestate",
					middleware.Authorize(apisecurity.PermAlarmUpdate, model.PermissionCan, enforcer),
					alarmActionAPI.BulkChangeState,
				)
			}

			alarmTagRouter := bulkRouter.Group("/alarm-tags")
			{
				alarmTagRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjAlarmTag, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					alarmTagAPI.BulkDelete,
				)
			}

			colorThemeRouter := bulkRouter.Group("/color-themes")
			{
				colorThemeRouter.DELETE(
					"",
					middleware.Authorize(apisecurity.ObjColorTheme, model.PermissionDelete, enforcer),
					middleware.PreProcessBulk(apiConfigProvider, false),
					colorThemeApi.BulkDelete,
				)
			}

			bulkRouter.DELETE(
				"/external-data-tables/:table/data",
				middleware.Authorize(apisecurity.ObjExternalDataTable, model.PermissionUpdate, enforcer),
				middleware.PreProcessBulk(apiConfigProvider, false),
				externalDataTableAPI.BulkDeleteData,
			)
		}

		dateStorageRouter := protected.Group("data-storage")
		{
			dateStorageAPI := datastorage.NewApi(datastorage.NewStore(primaryDbClient, pgPoolProvider, logger))
			dateStorageRouter.GET(
				"",
				middleware.Authorize(apisecurity.PermDataStorageRead, model.PermissionCan, enforcer),
				dateStorageAPI.Get,
			)
			dateStorageRouter.PUT(
				"",
				middleware.Authorize(apisecurity.PermDataStorageUpdate, model.PermissionCan, enforcer),
				dateStorageAPI.Update,
			)
		}

		messageRateStatsRouter := protected.Group("/message-rate-stats")
		{
			messageRateStatsAPI := messageratestats.NewApi(messageratestats.NewStore(pgPoolProvider))
			messageRateStatsRouter.GET(
				"",
				middleware.Authorize(apisecurity.PermMessageRateStatsRead, model.PermissionCan, enforcer),
				messageRateStatsAPI.List,
			)
		}

		fileRouter := protected.Group("/file")
		{
			fileAPI := file.NewApi(enforcer, file.NewStore(primaryDbClient, libfile.NewStorage(
				filepath.Join(conf.File.Dir, canopsis.SubDirUpload),
				libfile.NewEtagEncoder(),
			), conf.File.UploadMaxSize))
			fileRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjFile, model.PermissionCreate, enforcer),
				fileAPI.Create,
			)
			fileRouter.GET(
				"",
				fileAPI.List,
			)
			fileRouter.GET(
				"/:id",
				security.GetFileAuthMiddleware(),
				fileAPI.Get,
			)
			fileRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjFile, model.PermissionDelete, enforcer),
				fileAPI.Delete,
			)
		}

		iconsCacheMiddlewareGetter := middleware.NewCacheMiddlewareGetter(cacheExpiration, nil)
		iconsPath := "/icons"
		iconRouter := protected.Group(iconsPath)
		{
			iconStore := icon.NewStore(
				primaryDbClient,
				libfile.NewStorage(filepath.Join(conf.File.Dir, canopsis.SubDirIcons), libfile.NewEtagEncoder()),
			)
			iconApi := icon.NewApi(iconStore, websocketHub, conf.File.IconMaxSize, []string{mimeTypeSvg})
			iconRouter.POST(
				"",
				middleware.Authorize(apisecurity.PermIcon, model.PermissionCan, enforcer),
				iconApi.Create,
				iconsCacheMiddlewareGetter.ClearCache(BaseUrl+iconsPath),
			)
			iconRouter.GET(
				"",
				iconsCacheMiddlewareGetter.Cache(),
				iconApi.List,
			)
			iconRouter.GET(
				"/:id",
				security.GetFileAuthMiddleware(),
				iconsCacheMiddlewareGetter.Cache(),
				iconApi.Get,
			)
			iconRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.PermIcon, model.PermissionCan, enforcer),
				iconApi.Delete,
				iconsCacheMiddlewareGetter.ClearCache(BaseUrl+iconsPath),
			)
			iconRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.PermIcon, model.PermissionCan, enforcer),
				iconApi.Update,
				iconsCacheMiddlewareGetter.ClearCache(BaseUrl+iconsPath),
			)
			iconRouter.PATCH(
				"/:id",
				middleware.Authorize(apisecurity.PermIcon, model.PermissionCan, enforcer),
				iconApi.Patch,
				iconsCacheMiddlewareGetter.ClearCache(BaseUrl+iconsPath),
			)
		}

		resolveRuleRouter := protected.Group("/resolve-rules")
		{
			resolveRuleAPI := resolverule.NewApi(
				resolverule.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient)),
			)
			resolveRuleRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjResolveRule, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				resolveRuleAPI.Create,
			)
			resolveRuleRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjResolveRule, model.PermissionRead, enforcer),
				resolveRuleAPI.List,
			)
			resolveRuleRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjResolveRule, model.PermissionRead, enforcer),
				resolveRuleAPI.Get,
			)
			resolveRuleRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjResolveRule, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				resolveRuleAPI.Update,
			)
			resolveRuleRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjResolveRule, model.PermissionDelete, enforcer),
				resolveRuleAPI.Delete,
			)
		}

		flappingRuleAPI := flappingrule.NewApi(
			flappingrule.NewStore(primaryDbClient, authorProvider, common.NewPatternFieldsTransformer(primaryDbClient)),
			dbexport.NewExporter(primaryDbClient),
		)
		flappingRuleRouter := protected.Group("/flapping-rules")
		{
			flappingRuleRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjFlappingRule, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				flappingRuleAPI.Create,
			)
			flappingRuleRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjFlappingRule, model.PermissionRead, enforcer),
				flappingRuleAPI.List,
			)
			flappingRuleRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjFlappingRule, model.PermissionRead, enforcer),
				flappingRuleAPI.Get,
			)
			flappingRuleRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjFlappingRule, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				flappingRuleAPI.Update,
			)
			flappingRuleRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjFlappingRule, model.PermissionDelete, enforcer),
				flappingRuleAPI.Delete,
			)
		}
		protected.POST(
			"flapping-rules-db-export",
			middleware.Authorize(apisecurity.ObjFlappingRule, model.PermissionRead, enforcer),
			flappingRuleAPI.DBExport)

		entityInfoDictionaryApi := entityinfodictionary.NewApi(entityinfodictionary.NewStore(primaryDbClient), logger)
		protected.GET("/entity-infos-dictionary/keys",
			middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
			entityInfoDictionaryApi.ListKeys,
		)
		protected.GET("/entity-infos-dictionary/values",
			middleware.Authorize(apisecurity.ObjEntity, model.PermissionRead, enforcer),
			entityInfoDictionaryApi.ListValues,
		)

		techMetricsAPI := techmetrics.NewApi(techMetricsTaskExecutor, techmetrics.NewStore(primaryDbClient), timezoneConfigProvider)
		techMetricsRouter := protected.Group("/tech-metrics-export")
		{
			techMetricsRouter.POST(
				"",
				middleware.Authorize(apisecurity.PermTechMetrics, model.PermissionCan, enforcer),
				techMetricsAPI.StartExport,
			)
			techMetricsRouter.GET(
				"",
				middleware.Authorize(apisecurity.PermTechMetrics, model.PermissionCan, enforcer),
				techMetricsAPI.GetExport,
			)
			techMetricsRouter.GET(
				"/download",
				security.GetFileAuthMiddleware(),
				middleware.Authorize(apisecurity.PermTechMetrics, model.PermissionCan, enforcer),
				techMetricsAPI.DownloadExport,
			)
		}

		techMetricsSettingsRouter := protected.Group("/tech-metrics-settings")
		{
			techMetricsSettingsRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjTechMetricsSettings, model.PermissionRead, enforcer),
				techMetricsAPI.GetSettings,
			)
			techMetricsSettingsRouter.PUT(
				"",
				middleware.Authorize(apisecurity.ObjTechMetricsSettings, model.PermissionUpdate, enforcer),
				techMetricsAPI.UpdateSettings,
			)
		}

		templateValidatorApi := template.NewApi(validator.NewValidator(timezoneConfigProvider), templateConfigProvider)
		templateValidatorRouter := protected.Group("/template-validator")
		{
			templateValidatorRouter.POST(
				"/declare-ticket-rules",
				middleware.OnlyAuth(),
				templateValidatorApi.ValidateDeclareTicketRules,
			)
			templateValidatorRouter.POST(
				"/scenarios",
				middleware.OnlyAuth(),
				templateValidatorApi.ValidateScenarios,
			)
			templateValidatorRouter.POST(
				"/event-filter-rules",
				middleware.OnlyAuth(),
				templateValidatorApi.ValidateEventFilterRules,
			)
		}
		protected.GET(
			"/template-vars",
			templateValidatorApi.GetEnvVars,
		)

		maintenanceApi := maintenance.NewApi(
			maintenance.NewStore(
				primaryDbClient,
				userprovider.NewMongoProvider(primaryDbClient, apiConfigProvider),
				security.GetTokenService(),
				sessionStore,
			),
		)
		protected.PUT(
			"/maintenance",
			middleware.Authorize(apisecurity.PermMaintenance, model.PermissionCan, enforcer),
			maintenanceApi.Maintenance,
		)

		entityInfosPropertyAPI := entityinfosproperty.NewApi(entityinfosproperty.NewStore(primaryDbClient, authorProvider))
		entityInfosPropertyRouter := protected.Group("/entity-infos-properties")
		{
			entityInfosPropertyRouter.POST(
				"",
				middleware.Authorize(apisecurity.ObjEntityInfosProperty, model.PermissionCreate, enforcer),
				middleware.SetAuthor(),
				entityInfosPropertyAPI.Create,
			)
			entityInfosPropertyRouter.GET(
				"",
				middleware.Authorize(apisecurity.ObjEntityInfosProperty, model.PermissionRead, enforcer),
				entityInfosPropertyAPI.List,
			)
			entityInfosPropertyRouter.GET(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityInfosProperty, model.PermissionRead, enforcer),
				entityInfosPropertyAPI.Get,
			)
			entityInfosPropertyRouter.PUT(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityInfosProperty, model.PermissionUpdate, enforcer),
				middleware.SetAuthor(),
				entityInfosPropertyAPI.Update,
			)
			entityInfosPropertyRouter.DELETE(
				"/:id",
				middleware.Authorize(apisecurity.ObjEntityInfosProperty, model.PermissionDelete, enforcer),
				entityInfosPropertyAPI.Delete,
			)
		}
	}
}
